From 077c640095408b89cae93fd53249d5d0bcf9eb7b Mon Sep 17 00:00:00 2001 From: bux <33053305> Date: Tue, 13 Apr 2021 11:36:38 +0300 Subject: [PATCH 1/7] Add possibility to preserve files --- README.md | 14 ++++++++------ src/ftp-deploy.js | 2 +- src/ftp-deploy.spec.js | 1 + src/lib.js | 10 +++++----- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fda0465..40f5a02 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ var config = { exclude: ["dist/**/*.map", "node_modules/**", "node_modules/**/.*", ".git/**"], // delete ALL existing files at destination before uploading, if true deleteRemote: false, + // files on remote to be preserved, when deleteRemote is set to true + preserve: [".htaccess"], // Passive mode is forced (EPSV command is not sent) forcePasv: true, // use sftp or ftp @@ -44,9 +46,9 @@ ftpDeploy ``` **Note:** - - in version 2 the config file expects a field of `user` rather than `username` in 1.x. - - The config file is passed as-is to Promise-FTP. - - I create a file - e.g. deploy.js - in the root of my source code and add a script to its `package.json` so that I can `npm run deploy`. +- in version 2 the config file expects a field of `user` rather than `username` in 1.x. +- The config file is passed as-is to Promise-FTP. +- I create a file - e.g. deploy.js - in the root of my source code and add a script to its `package.json` so that I can `npm run deploy`. ```json "scripts": { @@ -54,7 +56,7 @@ ftpDeploy }, ``` - - You can use callback instead of promise. +- You can use callback instead of promise. ```js // use with callback @@ -110,5 +112,5 @@ npm test ## ToDo - - re-enable continueOnError - - update newer files only (PR welcome) +- re-enable continueOnError +- update newer files only (PR welcome) diff --git a/src/ftp-deploy.js b/src/ftp-deploy.js index 0b23da6..858134b 100644 --- a/src/ftp-deploy.js +++ b/src/ftp-deploy.js @@ -148,7 +148,7 @@ const FtpDeployer = function () { this.deleteRemote = (config) => { if (config.deleteRemote) { return lib - .deleteDir(this.ftp, config.remoteRoot) + .deleteDir(this.ftp, config.remoteRoot, config.preserve) .then(() => { this.emit("log", "Deleted directory: " + config.remoteRoot); return config; diff --git a/src/ftp-deploy.spec.js b/src/ftp-deploy.spec.js index 932daa1..21b8096 100644 --- a/src/ftp-deploy.spec.js +++ b/src/ftp-deploy.spec.js @@ -20,6 +20,7 @@ const config = { remoteRoot: "/ftp", exclude: [], include: ["folderA/**/*", "test-inside-root.txt"], + preserve: [], debugMode: true }; diff --git a/src/lib.js b/src/lib.js index 8d66eb1..6c8842b 100644 --- a/src/lib.js +++ b/src/lib.js @@ -111,15 +111,15 @@ function countFiles(filemap) { .length; } -function deleteDir(ftp, dir) { +function deleteDir(ftp, dir, preserve = []) { return ftp.list(dir).then(lst => { let dirNames = lst - .filter(f => f.type == "d" && f.name != ".." && f.name != ".") - .map(f => path.posix.join(dir, f.name)); + .filter(f => f.type == "d" && f.name != ".." && f.name != "." && !preserve.includes(f.name)) + .map(f => path.posix.join(dir, f.name)); let fnames = lst - .filter(f => f.type != "d") - .map(f => path.posix.join(dir, f.name)); + .filter(f => f.type != "d" && !preserve.includes(f.name)) + .map(f => path.posix.join(dir, f.name)); // delete sub-directories and then all files return Promise.mapSeries(dirNames, dirName => { From 9862753665c37d69c2cd66ed8e95508e69725ef6 Mon Sep 17 00:00:00 2001 From: a-tonchev Date: Thu, 11 May 2023 17:31:41 +0300 Subject: [PATCH 2/7] Add possibility to ignore files --- README.md | 2 ++ src/ftp-deploy.js | 3 ++- src/ftp-deploy.spec.js | 1 + src/lib.js | 34 +++++++++++++++++++++++++--------- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index abf1acc..1af32f2 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ const config = { "node_modules/**/.*", ".git/**", ], + // Preserve files that should be kept/ignored on remote directory + preserve: [".htaccess"], // delete ALL existing files at destination before uploading, if true deleteRemote: false, // Passive mode is forced (EPSV command is not sent) diff --git a/src/ftp-deploy.js b/src/ftp-deploy.js index 0b23da6..8882ae7 100644 --- a/src/ftp-deploy.js +++ b/src/ftp-deploy.js @@ -147,8 +147,9 @@ const FtpDeployer = function () { // Returns config this.deleteRemote = (config) => { if (config.deleteRemote) { + const preserve = config.preserve?.length ? config.preserve.map(p => config.remoteRoot + p) : []; return lib - .deleteDir(this.ftp, config.remoteRoot) + .deleteDir(this.ftp, config.remoteRoot, preserve) .then(() => { this.emit("log", "Deleted directory: " + config.remoteRoot); return config; diff --git a/src/ftp-deploy.spec.js b/src/ftp-deploy.spec.js index 12fa60b..bb02d3e 100644 --- a/src/ftp-deploy.spec.js +++ b/src/ftp-deploy.spec.js @@ -19,6 +19,7 @@ const config = { localRoot: path.join(__dirname, "../test/local"), remoteRoot: "/ftp", exclude: [], + preserve: [".htaccess"], include: ["folderA/**/*", "test-inside-root.txt"], debugMode: true, }; diff --git a/src/lib.js b/src/lib.js index 8d66eb1..04f835f 100644 --- a/src/lib.js +++ b/src/lib.js @@ -111,21 +111,37 @@ function countFiles(filemap) { .length; } -function deleteDir(ftp, dir) { +function deleteDir(ftp, dir, preserve = []) { return ftp.list(dir).then(lst => { + // console.log(dir, lst.map(l => dir + l.name)); let dirNames = lst - .filter(f => f.type == "d" && f.name != ".." && f.name != ".") - .map(f => path.posix.join(dir, f.name)); + .filter(f => f.type === "d" && f.name !== ".." && f.name !== ".") + .map(f => path.posix.join(dir, f.name)); let fnames = lst - .filter(f => f.type != "d") - .map(f => path.posix.join(dir, f.name)); + .filter(f => f.type !== "d") + .map(f => path.posix.join(dir, f.name)); - // delete sub-directories and then all files + // delete subdirectories and then all files return Promise.mapSeries(dirNames, dirName => { - // deletes everything in sub-directory, and then itself - return deleteDir(ftp, dirName).then(() => ftp.rmdir(dirName)); - }).then(() => Promise.mapSeries(fnames, fname => ftp.delete(fname))); + const preserveAllFilesOnDir = preserve.find(p => p === dirName); + const preserveDir = preserveAllFilesOnDir || preserve.find(p => p.startsWith(dirName + '/')); + // deletes everything in subdirectory if not preserved + if(!preserveAllFilesOnDir) { + return deleteDir(ftp, dirName, preserve).then(() => { + if(!preserveDir) { + ftp.rmdir(dirName) + } + }); + } + }).then(() => Promise.mapSeries(fnames, fname => { + const preserveFile = preserve.find(p => { + return p === fname + }); + if(!preserveFile) { + ftp.delete(fname) + } + })); }); } From c706a5e9bac3b1a9fa5ea77f7bd8c30f8f91f172 Mon Sep 17 00:00:00 2001 From: a-tonchev Date: Thu, 11 May 2023 17:32:20 +0300 Subject: [PATCH 3/7] Add possibility to ignore files --- src/lib.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.js b/src/lib.js index 04f835f..9c3e658 100644 --- a/src/lib.js +++ b/src/lib.js @@ -138,6 +138,7 @@ function deleteDir(ftp, dir, preserve = []) { const preserveFile = preserve.find(p => { return p === fname }); + // delete file if not preserved if(!preserveFile) { ftp.delete(fname) } From 248a44452d8b0a9186821f20e40a22a021b5cd05 Mon Sep 17 00:00:00 2001 From: "at.tonchev" Date: Wed, 5 Jul 2023 16:18:09 +0300 Subject: [PATCH 4/7] try to fix memory leak --- src/ftp-deploy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ftp-deploy.js b/src/ftp-deploy.js index 8882ae7..9147851 100644 --- a/src/ftp-deploy.js +++ b/src/ftp-deploy.js @@ -83,8 +83,8 @@ const FtpDeployer = function () { // so instead provide one ourselfs if (config.sftp) { this.connectionStatus = "disconnected"; - this.ftp.on("end", this.handleDisconnect); - this.ftp.on("close", this.handleDisconnect); + this.ftp.once("end", this.handleDisconnect); + this.ftp.once("close", this.handleDisconnect); } return this.ftp From 25d78bb773b7dd8180fdbd818338b9911560e452 Mon Sep 17 00:00:00 2001 From: cyrdam <51902761+cyrdam@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:17:48 +0100 Subject: [PATCH 5/7] feat: replace bluebird Promise with native Promise --- package-lock.json | 13 +++++++------ package.json | 7 +++++-- src/ftp-deploy.js | 5 ++--- src/lib.js | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff7f27d..d3881c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,14 @@ { "name": "ftp-deploy", - "version": "2.4.4", + "version": "2.4.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ftp-deploy", - "version": "2.4.4", + "version": "2.4.5", "license": "MIT", "dependencies": { - "bluebird": "^3.7.2", "minimatch": "9.0.0", "promise-ftp": "^1.3.5", "read": "^2.1.0", @@ -23,7 +22,7 @@ "mocha": "^9.1.3" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/@icetee/ftp": { @@ -162,7 +161,8 @@ "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -2033,7 +2033,8 @@ "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true }, "brace-expansion": { "version": "2.0.1", diff --git a/package.json b/package.json index 2f0f66f..2a60726 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "server": "node test/server.js" }, "dependencies": { - "bluebird": "^3.7.2", "minimatch": "9.0.0", "promise-ftp": "^1.3.5", "read": "^2.1.0", @@ -48,9 +47,13 @@ { "name": "keyle", "url": "https://github.com/keyle" + }, + { + "name": "cyrdam", + "url": "https://github.com/cyrdam" } ], "prettier": { "tabWidth": 4 } -} \ No newline at end of file +} diff --git a/src/ftp-deploy.js b/src/ftp-deploy.js index 0b23da6..2c69a07 100644 --- a/src/ftp-deploy.js +++ b/src/ftp-deploy.js @@ -3,7 +3,6 @@ const upath = require("upath"); const util = require("util"); const events = require("events"); -const Promise = require("bluebird"); const fs = require("fs"); var PromiseFtp = require("promise-ftp"); @@ -32,7 +31,7 @@ const FtpDeployer = function () { this.makeAllAndUpload = function (remoteDir, filemap) { let keys = Object.keys(filemap); - return Promise.mapSeries(keys, (key) => { + return lib.mapSeries(keys, (key) => { // console.log("Processing", key, filemap[key]); return this.makeAndUpload(remoteDir, key, filemap[key]); }); @@ -51,7 +50,7 @@ const FtpDeployer = function () { let newDirectory = upath.join(config.remoteRoot, relDir); return this.makeDir(newDirectory, true).then(() => { // console.log("newDirectory", newDirectory); - return Promise.mapSeries(fnames, (fname) => { + return lib.mapSeries(fnames, (fname) => { let tmpFileName = upath.join(config.localRoot, relDir, fname); let tmp = fs.readFileSync(tmpFileName); this.eventObject["filename"] = upath.join(relDir, fname); diff --git a/src/lib.js b/src/lib.js index 3ed34c6..fbb1d8a 100644 --- a/src/lib.js +++ b/src/lib.js @@ -1,7 +1,6 @@ const fs = require("fs"); const path = require("path"); const util = require("util"); -const Promise = require("bluebird"); const read = require("read"); const readP = util.promisify(read); @@ -122,10 +121,10 @@ function deleteDir(ftp, dir) { .map((f) => path.posix.join(dir, f.name)); // delete sub-directories and then all files - return Promise.mapSeries(dirNames, (dirName) => { + return mapSeries(dirNames, (dirName) => { // deletes everything in sub-directory, and then itself return deleteDir(ftp, dirName).then(() => ftp.rmdir(dirName)); - }).then(() => Promise.mapSeries(fnames, (fname) => ftp.delete(fname))); + }).then(() => mapSeries(fnames, (fname) => ftp.delete(fname))); }); } @@ -142,6 +141,33 @@ mkDirExists = (ftp, dir) => { }); }; +/** + * Applies the mapper function to each item in the array in series. + * @param {Array} array - The array to map over. + * @param {Function} mapper - The mapping function. + * @returns {Promise} A promise that resolves with the new array. + */ +function mapSeries(array, mapper) { + // Initialize a resolved Promise + let result = Promise.resolve(); + // Empty array to store the results + const output = []; + + array.forEach((item, index) => { + // Chain a new Promise to the result + result = result.then(() => { + // Apply the mapper function to the current item + return mapper(item, index, array) + .then(res => { + output.push(res); + }); + }); + }); + + // Return a Promise that resolves with the output array + return result.then(() => output); +} + module.exports = { checkIncludes: checkIncludes, getPassword: getPassword, @@ -150,4 +176,5 @@ module.exports = { countFiles: countFiles, mkDirExists: mkDirExists, deleteDir: deleteDir, + mapSeries: mapSeries, }; From 8e2fdcb21bfd34e71bf5fad9c0ba21069da725a7 Mon Sep 17 00:00:00 2001 From: a-tonchev Date: Mon, 18 Dec 2023 14:51:20 +0200 Subject: [PATCH 6/7] Remove bluebird merge --- src/ftp-deploy.js.bak | 203 ++++++++++++++++++++++++++++++++++++++++++ src/lib.js.bak | 178 ++++++++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 src/ftp-deploy.js.bak create mode 100644 src/lib.js.bak diff --git a/src/ftp-deploy.js.bak b/src/ftp-deploy.js.bak new file mode 100644 index 0000000..4a331cd --- /dev/null +++ b/src/ftp-deploy.js.bak @@ -0,0 +1,203 @@ +"use strict"; + +const upath = require("upath"); +const util = require("util"); +const events = require("events"); +const Promise = require("bluebird"); +const fs = require("fs"); + +var PromiseFtp = require("promise-ftp"); +var PromiseSftp = require("ssh2-sftp-client"); +const lib = require("./lib"); + +/* interim structure +{ + '/': ['test-inside-root.txt'], + 'folderA': ['test-inside-a.txt'], + 'folderA/folderB': ['test-inside-b.txt'], + 'folderA/folderB/emptyC': [], + 'folderA/folderB/emptyC/folderD': ['test-inside-d-1.txt', 'test-inside-d-2.txt'] +} +*/ + +const FtpDeployer = function () { + // The constructor for the super class. + events.EventEmitter.call(this); + this.ftp = null; + this.eventObject = { + totalFilesCount: 0, + transferredFileCount: 0, + filename: "", + }; + + this.makeAllAndUpload = function (remoteDir, filemap) { + let keys = Object.keys(filemap); + return Promise.mapSeries(keys, (key) => { + // console.log("Processing", key, filemap[key]); + return this.makeAndUpload(remoteDir, key, filemap[key]); + }); + }; + + this.makeDir = function (newDirectory) { + if (newDirectory === "/") { + return Promise.resolve("unused"); + } else { + return this.ftp.mkdir(newDirectory, true); + } + }; + // Creates a remote directory and uploads all of the files in it + // Resolves a confirmation message on success + this.makeAndUpload = (config, relDir, fnames) => { + let newDirectory = upath.join(config.remoteRoot, relDir); + return this.makeDir(newDirectory, true).then(() => { + // console.log("newDirectory", newDirectory); + return Promise.mapSeries(fnames, (fname) => { + let tmpFileName = upath.join(config.localRoot, relDir, fname); + let tmp = fs.readFileSync(tmpFileName); + this.eventObject["filename"] = upath.join(relDir, fname); + + this.emit("uploading", this.eventObject); + + return this.ftp + .put(tmp, upath.join(config.remoteRoot, relDir, fname)) + .then(() => { + this.eventObject.transferredFileCount++; + this.emit("uploaded", this.eventObject); + return Promise.resolve("uploaded " + tmpFileName); + }) + .catch((err) => { + this.eventObject["error"] = err; + this.emit("upload-error", this.eventObject); + // if continue on error.... + return Promise.reject(err); + }); + }); + }); + }; + + // connects to the server, Resolves the config on success + this.connect = (config) => { + this.ftp = config.sftp ? new PromiseSftp() : new PromiseFtp(); + + // sftp client does not provide a connection status + // so instead provide one ourselfs + if (config.sftp) { + this.connectionStatus = "disconnected"; + this.ftp.once("end", this.handleDisconnect); + this.ftp.once("close", this.handleDisconnect); + } + + return this.ftp + .connect(config) + .then((serverMessage) => { + this.emit("log", "Connected to: " + config.host); + this.emit("log", "Connected: Server message: " + serverMessage); + + // sftp does not provide a connection status + // so instead provide one ourself + if (config.sftp) { + this.connectionStatus = "connected"; + } + + return config; + }) + .catch((err) => { + return Promise.reject({ + code: err.code, + message: "connect: " + err.message, + }); + }); + }; + + this.getConnectionStatus = () => { + // only ftp client provides connection status + // sftp client connection status is handled using events + return typeof this.ftp.getConnectionStatus === "function" + ? this.ftp.getConnectionStatus() + : this.connectionStatus; + }; + + this.handleDisconnect = () => { + this.connectionStatus = "disconnected"; + }; + + // creates list of all files to upload and starts upload process + this.checkLocalAndUpload = (config) => { + try { + let filemap = lib.parseLocal( + config.include, + config.exclude, + config.localRoot, + "/" + ); + // console.log(filemap); + this.emit( + "log", + "Files found to upload: " + JSON.stringify(filemap) + ); + this.eventObject["totalFilesCount"] = lib.countFiles(filemap); + + return this.makeAllAndUpload(config, filemap); + } catch (e) { + return Promise.reject(e); + } + }; + + // Deletes remote directory if requested by config + // Returns config + this.deleteRemote = (config) => { + if (config.deleteRemote) { + const preserve = config.preserve?.length ? config.preserve.map(p => config.remoteRoot + p) : []; + return lib +<<<<<<< HEAD + .deleteDir(this.ftp, config.remoteRoot, config.preserve) +======= + .deleteDir(this.ftp, config.remoteRoot, preserve) +>>>>>>> b921f362763aa7ae4243fddc464d189d4230ddd8 + .then(() => { + this.emit("log", "Deleted directory: " + config.remoteRoot); + return config; + }) + .catch((err) => { + this.emit( + "log", + "Deleting failed, trying to continue: " + + JSON.stringify(err) + ); + return Promise.resolve(config); + }); + } + return Promise.resolve(config); + }; + + this.deploy = function (config, cb) { + return lib + .checkIncludes(config) + .then(lib.getPassword) + .then(this.connect) + .then(this.deleteRemote) + .then(this.checkLocalAndUpload) + .then((res) => { + this.ftp.end(); + if (typeof cb == "function") { + cb(null, res); + } else { + return Promise.resolve(res); + } + }) + .catch((err) => { + console.log("Err", err.message); + if (this.ftp && this.getConnectionStatus() != "disconnected") + this.ftp.end(); + if (typeof cb == "function") { + cb(err, null); + } else { + return Promise.reject(err); + } + }); + }; +}; + +util.inherits(FtpDeployer, events.EventEmitter); + +module.exports = FtpDeployer; diff --git a/src/lib.js.bak b/src/lib.js.bak new file mode 100644 index 0000000..b437309 --- /dev/null +++ b/src/lib.js.bak @@ -0,0 +1,178 @@ +const fs = require("fs"); +const path = require("path"); +const util = require("util"); +const Promise = require("bluebird"); + +const read = require("read"); +const readP = util.promisify(read); + +const { minimatch } = require("minimatch"); + +// P H A S E 0 + +function checkIncludes(config) { + config.excludes = config.excludes || []; + if (!config.include || !config.include.length) { + return Promise.reject({ + code: "NoIncludes", + message: "You need to specify files to upload - e.g. ['*', '**/*']", + }); + } else { + return Promise.resolve(config); + } +} + +function getPassword(config) { + if (config.password) { + return Promise.resolve(config); + } else { + let options = { + prompt: + "Password for " + + config.user + + "@" + + config.host + + " (ENTER for none): ", + default: "", + silent: true, + }; + return readP(options).then((res) => { + let config2 = Object.assign(config, { password: res }); + return config2; + }); + } +} + +// Analysing local firstory + +function canIncludePath(includes, excludes, filePath) { + let go = (acc, item) => + acc || minimatch(filePath, item, { matchBase: true }); + let canInclude = includes.reduce(go, false); + + // Now check whether the file should in fact be specifically excluded + if (canInclude) { + // if any excludes match return false + if (excludes) { + let go2 = (acc, item) => + acc && !minimatch(filePath, item, { matchBase: true }); + canInclude = excludes.reduce(go2, true); + } + } + // console.log("canIncludePath", include, filePath, res); + return canInclude; +} + +// A method for parsing the source location and storing the information into a suitably formated object +function parseLocal(includes, excludes, localRootDir, relDir) { + // reducer + let handleItem = function (acc, item) { + const currItem = path.join(fullDir, item); + const newRelDir = path.relative(localRootDir, currItem); + + if (fs.lstatSync(currItem).isDirectory()) { + // currItem is a directory. Recurse and attach to accumulator + let tmp = parseLocal(includes, excludes, localRootDir, newRelDir); + for (let key in tmp) { + if (tmp[key].length == 0) { + delete tmp[key]; + } + } + return Object.assign(acc, tmp); + } else { + // currItem is a file + // acc[relDir] is always created at previous iteration + if (canIncludePath(includes, excludes, newRelDir)) { + // console.log("including", currItem); + acc[relDir].push(item); + return acc; + } + } + return acc; + }; + + const fullDir = path.join(localRootDir, relDir); + // Check if `startDir` is a valid location + if (!fs.existsSync(fullDir)) { + throw new Error(fullDir + " is not an existing location"); + } + + // Iterate through the contents of the `fullDir` of the current iteration + const files = fs.readdirSync(fullDir); + // Add empty array, which may get overwritten by subsequent iterations + let acc = {}; + acc[relDir] = []; + const res = files.reduce(handleItem, acc); + return res; +} + +function countFiles(filemap) { + return Object.values(filemap).reduce((acc, item) => acc.concat(item)) + .length; +} + +function deleteDir(ftp, dir, preserve = []) { + return ftp.list(dir).then(lst => { + // console.log(dir, lst.map(l => dir + l.name)); + let dirNames = lst +<<<<<<< HEAD + .filter(f => f.type == "d" && f.name != ".." && f.name != "." && !preserve.includes(f.name)) + .map(f => path.posix.join(dir, f.name)); + + let fnames = lst + .filter(f => f.type != "d" && !preserve.includes(f.name)) +======= + .filter(f => f.type === "d" && f.name !== ".." && f.name !== ".") + .map(f => path.posix.join(dir, f.name)); + + let fnames = lst + .filter(f => f.type !== "d") +>>>>>>> b921f362763aa7ae4243fddc464d189d4230ddd8 + .map(f => path.posix.join(dir, f.name)); + + // delete subdirectories and then all files + return Promise.mapSeries(dirNames, dirName => { + const preserveAllFilesOnDir = preserve.find(p => p === dirName); + const preserveDir = preserveAllFilesOnDir || preserve.find(p => p.startsWith(dirName + '/')); + // deletes everything in subdirectory if not preserved + if(!preserveAllFilesOnDir) { + return deleteDir(ftp, dirName, preserve).then(() => { + if(!preserveDir) { + ftp.rmdir(dirName) + } + }); + } + }).then(() => Promise.mapSeries(fnames, fname => { + const preserveFile = preserve.find(p => { + return p === fname + }); + // delete file if not preserved + if(!preserveFile) { + ftp.delete(fname) + } + })); + }); +} + +mkDirExists = (ftp, dir) => { + // Make the directory using recursive expand + return ftp.mkdir(dir, true).catch((err) => { + if (err.message.startsWith("EEXIST")) { + return Promise.resolve(); + } else { + console.log("[mkDirExists]", err.message); + // console.log(Object.getOwnPropertyNames(err)); + return Promise.reject(err); + } + }); +}; + +module.exports = { + checkIncludes: checkIncludes, + getPassword: getPassword, + parseLocal: parseLocal, + canIncludePath: canIncludePath, + countFiles: countFiles, + mkDirExists: mkDirExists, + deleteDir: deleteDir, +}; From 537f5ded070308c94d815349c69adce929391e3a Mon Sep 17 00:00:00 2001 From: a-tonchev Date: Mon, 18 Dec 2023 15:07:25 +0200 Subject: [PATCH 7/7] Replace Replace --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b9e7b61..af27a8a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ playground notes.md .env dist +.idea/workspace.xml