From aa15a9c64ef7a2f1f9e864b720585f39cc14a10a Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Tue, 13 Dec 2022 14:52:55 +0545 Subject: [PATCH 01/32] Added cordova file functionality to electron --- plugin.xml | 16 + scripts/electron/expose-node-modules.js | 22 + scripts/electron/main-function-string.js | 15 + scripts/electron/preload-function-string.js | 62 +++ scripts/electron/remove-added-code.js | 33 ++ src/electron/FileProxy.js | 504 ++++++++++++++++++++ 6 files changed, 652 insertions(+) create mode 100644 scripts/electron/expose-node-modules.js create mode 100644 scripts/electron/main-function-string.js create mode 100644 scripts/electron/preload-function-string.js create mode 100644 scripts/electron/remove-added-code.js create mode 100644 src/electron/FileProxy.js diff --git a/plugin.xml b/plugin.xml index 7c23c6df..c89ac30a 100644 --- a/plugin.xml +++ b/plugin.xml @@ -262,4 +262,20 @@ to config.xml in order for the application to find previously stored files. + + + + + + + + + + + + + + + + diff --git a/scripts/electron/expose-node-modules.js b/scripts/electron/expose-node-modules.js new file mode 100644 index 00000000..55d6f6c2 --- /dev/null +++ b/scripts/electron/expose-node-modules.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +const path = require('path'); + +module.exports = function (context) { + const { projectRoot } = context.opts; + + // preload + const preloadFunctionFileSource = path.resolve(projectRoot+'/plugins/cordova-plugin-file/scripts/electron/preload-function-string.js'); + const preloadFileSourceString = fs.readFileSync(preloadFunctionFileSource, {encoding: 'utf-8'}); + const preloadFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-preload.js'); + let preloadFileDestString = fs.readFileSync(preloadFunctionFileDest, {encoding: 'utf-8'}); + preloadFileDestString += preloadFileSourceString; + fs.writeFileSync(preloadFunctionFileDest, preloadFileDestString, {encoding: 'utf-8'}); + + // main + const mainFunctionFileSource = path.resolve(projectRoot+'/plugins/cordova-plugin-file/scripts/electron/main-function-string.js'); + const mainFileSourceString = fs.readFileSync(mainFunctionFileSource, {encoding: 'utf-8'}); + const mainFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-main.js'); + let mainFileDestString = fs.readFileSync(mainFunctionFileDest, {encoding: 'utf-8'}); + mainFileDestString += mainFileSourceString; + fs.writeFileSync(mainFunctionFileDest, mainFileDestString, {encoding: 'utf-8'}); +} \ No newline at end of file diff --git a/scripts/electron/main-function-string.js b/scripts/electron/main-function-string.js new file mode 100644 index 00000000..3e5e34ed --- /dev/null +++ b/scripts/electron/main-function-string.js @@ -0,0 +1,15 @@ + +/*Beginning of Main function additions for cordova.plugin.file*/ + +ipcMain.on('paths-prefix', (event) => { + const path = require('path'); + event.returnValue = { + applicationDirectory: path.dirname(app.getAppPath()) + path.sep, + dataDirectory: app.getPath('userData') + path.sep, + cacheDirectory: app.getPath('cache') + path.sep, + tempDirectory: app.getPath('temp') + path.sep, + documentsDirectory: app.getPath('documents') + path.sep + }; +}); + +/*End of Main function additions for cordova.plugin.file*/ \ No newline at end of file diff --git a/scripts/electron/preload-function-string.js b/scripts/electron/preload-function-string.js new file mode 100644 index 00000000..3f5ecfe0 --- /dev/null +++ b/scripts/electron/preload-function-string.js @@ -0,0 +1,62 @@ + +/*Beginning of Main function additions for cordova.plugin.file*/ +const fs = require('fs'); +contextBridge.exposeInMainWorld('cordovaFilePlugin', { + fs: require('fs'), + path: require('path'), + util: require('util'), + appPaths: ipcRenderer.sendSync('paths-prefix'), + copyFile: (srcPath, destPath, callback) => { + fs.copyFile(srcPath, destPath, callback); + }, + readFile: (path, callback) => { + fs.readdir(path, {withFileTypes: true}, (err, files) => { + if(files && files.length) { + files.forEach(file => { + file.isDirectory = file.isDirectory(); + file.isFile = file.isFile(); + }); + } + callback(err, files); + }); + }, + stat: (path, callback) => { + fs.stat(path, (err, stats) => { + if(stats) { + stats.isDirectory = stats.isDirectory(); + stats.isFile = stats.isFile(); + } + callback(err, stats); + }); + }, + open: (path, flags, callback) => { + fs.open(path, flags, callback); + }, + close: (fd, errorCallback) => { + fs.close(fd, errorCallback); + }, + utimes: (path, atime, mtime, callback) => { + fs.utimes(path, atime, mtime, callback); + }, + rm: (path, callback) => { + fs.rmSync(path, { recursive: true } ,callback); + }, + truncate: (path, size, callback) => { + fs.truncate(path, size, callback); + }, + mkdir: (path, callback) => { + fs.mkdir(path, callback); + }, + write: (fd, data, position, callback) => { + const buffer = Buffer.from(data); + fs.write(fd, buffer, 0, buffer.length, position, callback ); + }, + bufferAlloc: (size) => { + return Buffer.alloc(size); + }, + read: (fd, buffer, position, callback) => { + fs.read(fd, buffer, 0, buffer.length, position, callback) + } +}); + +/*End of Main function additions for cordova.plugin.file*/ \ No newline at end of file diff --git a/scripts/electron/remove-added-code.js b/scripts/electron/remove-added-code.js new file mode 100644 index 00000000..8da599b7 --- /dev/null +++ b/scripts/electron/remove-added-code.js @@ -0,0 +1,33 @@ +preloadCommentStartString = '/*Beginning of Main function additions for cordova.plugin.file*/'; +preloadCommentEndString = '/*End of Main function additions for cordova.plugin.file*/'; + +mainCommentStartString = '/*Beginning of Main function additions for cordova.plugin.file*/'; +mainCommentEndString = '/*End of Main function additions for cordova.plugin.file*/'; + +const fs = require('fs'); +const path = require('path'); + + +module.exports = function (context) { + const { projectRoot } = context.opts; + + // preload + const preloadFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-preload.js'); + let fileDestString = fs.readFileSync(preloadFunctionFileDest, {encoding: 'utf-8'}); + const preloadStartIndex = fileDestString.indexOf(preloadCommentStartString); + const preloadEndIndex = fileDestString.indexOf(preloadCommentEndString) + preloadCommentEndString.length; + if(preloadStartIndex > 0) { + fileDestString = fileDestString.substring(0, preloadStartIndex) + fileDestString.substring(preloadEndIndex); + fs.writeFileSync(preloadFunctionFileDest, fileDestString.toString(), {encoding: 'utf-8'}); + } + + // main + const mainFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-main.js'); + let mainFileDestString = fs.readFileSync(mainFunctionFileDest, {encoding: 'utf-8'}); + const mainStartIndex = mainFileDestString.indexOf(mainCommentStartString); + const mainEndIndex = mainFileDestString.indexOf(mainCommentEndString) + mainCommentEndString.length; + if(mainStartIndex > 0) { + mainFileDestString = mainFileDestString.substring(0, mainStartIndex) + mainFileDestString.substring(mainEndIndex); + fs.writeFileSync(mainFunctionFileDest, mainFileDestString.toString(), {encoding: 'utf-8'}); + } +} diff --git a/src/electron/FileProxy.js b/src/electron/FileProxy.js new file mode 100644 index 00000000..61687102 --- /dev/null +++ b/src/electron/FileProxy.js @@ -0,0 +1,504 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +(function () { + /* global require, exports, module */ + + if (window.cordovaFilePlugin === undefined) { + console.error('Something went wrong while installing the plugin. Please remove the plugin and install it again.'); + return; + } + + const nodePath = window.cordovaFilePlugin.path; + + const FileEntry = require('./FileEntry'); + const FileError = require('./FileError'); + const DirectoryEntry = require('./DirectoryEntry'); + const File = require('./File'); + + (function (exports, global) { + const pathsPrefix = window.cordovaFilePlugin.appPaths; + /** * Exported functionality ***/ + + // list a directory's contents (files and folders). + exports.readEntries = function (successCallback, errorCallback, args) { + const fullPath = args[0]; + + if (typeof successCallback !== 'function') { + throw Error('Expected successCallback argument.'); + } + + window.cordovaFilePlugin.readFile(fullPath, (err, files) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + return; + } + const result = []; + files.forEach(d => { + let path = fullPath + d.name; + if (d.isDirectory) { + path += nodePath.sep; + } + result.push({ + isDirectory: d.isDirectory, + isFile: d.isFile, + name: d.name, + fullPath: path, + filesystemName: 'temporary', + nativeURL: path + }); + }); + successCallback(result); + }); + }; + + exports.getFile = function (successCallback, errorCallback, args) { + const path = args[0] + args[1]; + const options = args[2] || {}; + + window.cordovaFilePlugin.stat(path, (err, stats) => { + if (err && err.message && err.message.indexOf('ENOENT') !== 0) { + if (errorCallback) { + errorCallback(FileError.INVALID_STATE_ERR); + } + return; + } + const exists = !err; + const baseName = window.cordovaFilePlugin.path.basename(path); + + function createFile () { + window.cordovaFilePlugin.open(path, 'w', (err, fd) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.INVALID_STATE_ERR); + } + return; + } + window.cordovaFilePlugin.close(fd, (err) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.INVALID_STATE_ERR); + } + return; + } + successCallback(new FileEntry(baseName, path)); + }); + }); + } + + if (options.create === true && options.exclusive === true && exists) { + // If create and exclusive are both true, and the path already exists, + // getFile must fail. + if (errorCallback) { + errorCallback(FileError.PATH_EXISTS_ERR); + } + } else if (options.create === true && !exists) { + // If create is true, the path doesn't exist, and no other error occurs, + // getFile must create it as a zero-length file and return a corresponding + // FileEntry. + createFile(); + } else if (options.create === true && exists) { + if (stats.isFile) { + // Overwrite file, delete then create new. + createFile(); + } else { + if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + } + } else if (!options.create && !exists) { + // If create is not true and the path doesn't exist, getFile must fail. + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + } else if (!options.create && exists && stats.isDirectory) { + // If create is not true and the path exists, but is a directory, getFile + // must fail. + if (errorCallback) { + errorCallback(FileError.TYPE_MISMATCH_ERR); + } + } else { + // Otherwise, if no other error occurs, getFile must return a FileEntry + // corresponding to path. + successCallback(new FileEntry(baseName, path)); + } + }); + }; + + exports.getFileMetadata = function (successCallback, errorCallback, args) { + const fullPath = args[0]; + window.cordovaFilePlugin.stat(fullPath, (err, stats) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + return; + } + const baseName = window.cordovaFilePlugin.path.basename(fullPath); + successCallback(new File(baseName, fullPath, '', stats.mtime, stats.size)); + }); + }; + + exports.getMetadata = function (successCallback, errorCallback, args) { + window.cordovaFilePlugin.stat(args[0], (err, stats) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + return; + } + successCallback({ + modificationTime: stats.mtime, + size: stats.size + }); + }); + }; + + exports.setMetadata = function (successCallback, errorCallback, args) { + const fullPath = args[0]; + const metadataObject = args[1]; + + window.cordovaFilePlugin.utimes(fullPath, metadataObject.modificationTime, metadataObject.modificationTime, (err) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + return; + } + successCallback(); + }); + }; + + exports.write = function (successCallback, errorCallback, args) { + const fileName = args[0]; + const data = args[1]; + const position = args[2]; + const isBinary = args[3]; // eslint-disable-line no-unused-vars + + if (!data) { + if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + return; + } + + const promisify = window.cordovaFilePlugin.util.promisify; + let bytesWritten = 0; + promisify(window.cordovaFilePlugin.open)(fileName, 'a') + .then(fd => { + return promisify(window.cordovaFilePlugin.write)(fd, data, position) + .then(bw => { bytesWritten = bw; }) + .catch(err => console.log(err)) + .finally(() => promisify(window.cordovaFilePlugin.close)(fd)); + }) + .then(() => successCallback(bytesWritten)) + .catch((err) => { + if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + }); + }; + + exports.readAsText = function (successCallback, errorCallback, args) { + const fileName = args[0]; + const enc = args[1]; + const startPos = args[2]; + const endPos = args[3]; + + readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsDataURL = function (successCallback, errorCallback, args) { + const fileName = args[0]; + const startPos = args[1]; + const endPos = args[2]; + + readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsBinaryString = function (successCallback, errorCallback, args) { + const fileName = args[0]; + const startPos = args[1]; + const endPos = args[2]; + + readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.readAsArrayBuffer = function (successCallback, errorCallback, args) { + const fileName = args[0]; + const startPos = args[1]; + const endPos = args[2]; + + readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback); + }; + + exports.remove = function (successCallback, errorCallback, args) { + const fullPath = args[0]; + + window.cordovaFilePlugin.stat(fullPath, (err, stats) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + return; + } + window.cordovaFilePlugin.rm(fullPath, (err) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NO_MODIFICATION_ALLOWED_ERR); + } + return; + } + successCallback(); + }); + }); + }; + + exports.truncate = function (successCallback, errorCallback, args) { + const fullPath = args[0]; + const size = args[1]; + + window.cordovaFilePlugin.truncate(fullPath, size, err => { + if (err) { + if (errorCallback) { + errorCallback(FileError.INVALID_STATE_ERR); + } + return; + } + successCallback(size); + }); + }; + + exports.removeRecursively = function (successCallback, errorCallback, args) { + const fullPath = args[0]; + window.cordovaFilePlugin.rm(fullPath, (err) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NO_MODIFICATION_ALLOWED_ERR); + } + return; + } + successCallback(); + }); + }; + + exports.getDirectory = function (successCallback, errorCallback, args) { + const path = args[0] + args[1]; + const options = args[2] || {}; + + window.cordovaFilePlugin.stat(path, (err, stats) => { + if (err && err.message && err.message.indexOf('ENOENT') !== 0) { + if (errorCallback) { + errorCallback(FileError.INVALID_STATE_ERR); + } + return; + } + const exists = !err; + const baseName = window.cordovaFilePlugin.path.basename(path); + + if (options.create === true && options.exclusive === true && exists) { + // If create and exclusive are both true, and the path already exists, + // getDirectory must fail. + if (errorCallback) { + errorCallback(FileError.PATH_EXISTS_ERR); + } + } else if (options.create === true && !exists) { + // If create is true, the path doesn't exist, and no other error occurs, + // getDirectory must create it as a zero-length file and return a corresponding + // MyDirectoryEntry. + window.cordovaFilePlugin.mkdir(path, (err) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.PATH_EXISTS_ERR); + } + return; + } + successCallback(new DirectoryEntry(baseName, path)); + }); + } else if (options.create === true && exists) { + if (stats.isDirectory) { + successCallback(new DirectoryEntry(baseName, path)); + } else if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + } else if (!options.create && !exists) { + // If create is not true and the path doesn't exist, getDirectory must fail. + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + } else if (!options.create && exists && stats.isFile) { + // If create is not true and the path exists, but is a file, getDirectory + // must fail. + if (errorCallback) { + errorCallback(FileError.TYPE_MISMATCH_ERR); + } + } else { + // Otherwise, if no other error occurs, getDirectory must return a + // DirectoryEntry corresponding to path. + successCallback(new DirectoryEntry(baseName, path)); + } + }); + }; + + exports.getParent = function (successCallback, errorCallback, args) { + if (typeof successCallback !== 'function') { + throw Error('Expected successCallback argument.'); + } + + const parentPath = nodePath.dirname(args[0]); + const parentName = nodePath.basename(parentPath); + const path = nodePath.dirname(parentPath) + nodePath.sep; + + exports.getDirectory(successCallback, errorCallback, [path, parentName, {create: false}]); + }; + + exports.copyTo = function (successCallback, errorCallback, args) { + const srcPath = args[0]; + const dstDir = args[1]; + const dstName = args[2]; + + window.cordovaFilePlugin.copyFile(srcPath, dstDir + dstName, (err) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.INVALID_MODIFICATION_ERR); + } + return; + } + exports.getFile(successCallback, errorCallback, [dstDir, dstName]); + }); + }; + + exports.moveTo = function (successCallback, errorCallback, args) { + const srcPath = args[0]; + // parentFullPath and name parameters is ignored because + // args is being passed downstream to exports.copyTo method + const parentFullPath = args[1]; // eslint-disable-line + const name = args[2]; // eslint-disable-line + + exports.copyTo(function (fileEntry) { + + exports.remove(function () { + successCallback(fileEntry); + }, errorCallback, [srcPath]); + + }, errorCallback, args); + }; + + exports.resolveLocalFileSystemURI = function (successCallback, errorCallback, args) { + let path = args[0]; + + // support for encodeURI + if (/\%5/g.test(path) || /\%20/g.test(path)) { // eslint-disable-line no-useless-escape + path = decodeURI(path); + } + + // support for cdvfile + if (path.trim().substr(0, 7) === 'cdvfile') { + if (path.indexOf('cdvfile://localhost') === -1) { + if (errorCallback) { + errorCallback(FileError.ENCODING_ERR); + } + return; + } + + const indexApplication = path.indexOf('application'); + const indexPersistent = path.indexOf('persistent'); + const indexTemporary = path.indexOf('temporary'); + + if (indexApplication !== -1) { // cdvfile://localhost/application/path/to/file + path = pathsPrefix.applicationDirectory + path.substr(indexApplication + 12); + } else if (indexPersistent !== -1) { // cdvfile://localhost/persistent/path/to/file + path = pathsPrefix.dataDirectory + path.substr(indexPersistent + 11); + } else if (indexTemporary !== -1) { // cdvfile://localhost/temporary/path/to/file + path = pathsPrefix.tempDirectory + path.substr(indexTemporary + 10); + } else { + if (errorCallback) { + errorCallback(FileError.ENCODING_ERR); + } + return; + } + } + + window.cordovaFilePlugin.stat(path, (err, stats) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + return; + } + + const baseName = window.cordovaFilePlugin.path.basename(path); + if (stats.isDirectory) { + successCallback(new DirectoryEntry(baseName, path)); + } else { + successCallback(new FileEntry(baseName, path)); + } + }); + }; + + exports.requestAllPaths = function (successCallback) { + successCallback(pathsPrefix); + }; + + /** * Helpers ***/ + + function readAs (what, fullPath, encoding, startPos, endPos, successCallback, errorCallback) { + const promisify = window.cordovaFilePlugin.util.promisify; + + window.cordovaFilePlugin.open(fullPath, 'r', (err, fd) => { + if (err) { + if (errorCallback) { + errorCallback(FileError.NOT_FOUND_ERR); + } + return; + } + const buf = window.cordovaFilePlugin.bufferAlloc(endPos - startPos); + promisify(window.cordovaFilePlugin.read)(fd, buf, startPos) + .then(() => { + switch (what) { + case 'text': + successCallback(buf.toString(encoding)); + break; + case 'dataURL': + successCallback('data:;base64,' + buf.toString('base64')); + break; + case 'arrayBuffer': + successCallback(buf); + break; + case 'binaryString': + successCallback(buf.toString('binary')); + break; + } + }) + .catch(() => { + if (errorCallback) { + errorCallback(FileError.NOT_READABLE_ERR); + } + }) + .then(() => promisify(fs.close)(fd)); + }); + } + + })(module.exports, window); + + require('cordova/exec/proxy').add('File', module.exports); +})(); \ No newline at end of file From 0b064faecc4b33f53ac847a42e381347ee0788df Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Thu, 22 Dec 2022 21:26:03 +0545 Subject: [PATCH 02/32] Ported complex implementation to a simpler one --- .gitignore | 3 + package.json | 7 +- plugin.xml | 13 +- scripts/electron/expose-node-modules.js | 22 - scripts/electron/main-function-string.js | 15 - scripts/electron/preload-function-string.js | 62 --- scripts/electron/remove-added-code.js | 33 -- src/electron/FileProxy.js | 504 -------------------- src/electron/index.js | 437 +++++++++++++++++ src/electron/package.json | 20 + 10 files changed, 468 insertions(+), 648 deletions(-) delete mode 100644 scripts/electron/expose-node-modules.js delete mode 100644 scripts/electron/main-function-string.js delete mode 100644 scripts/electron/preload-function-string.js delete mode 100644 scripts/electron/remove-added-code.js delete mode 100644 src/electron/FileProxy.js create mode 100644 src/electron/index.js create mode 100644 src/electron/package.json diff --git a/.gitignore b/.gitignore index 06994923..0c318211 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ Thumbs.db /.project node_modules + +src/electron/node_modules +src/electron/package-lock.json \ No newline at end of file diff --git a/package.json b/package.json index 2b21363f..9a4cdbee 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "platforms": [ "android", "browser", + "electron", "ios", "osx", "windows" @@ -23,7 +24,8 @@ "cordova-browser", "cordova-ios", "cordova-osx", - "cordova-windows" + "cordova-windows", + "cordova-electron" ], "scripts": { "test": "npm run lint", @@ -37,7 +39,8 @@ "cordova-android": ">=6.3.0" }, "7.0.0": { - "cordova-android": ">=10.0.0" + "cordova-android": ">=10.0.0", + "cordova-electron": ">=3.0.0" }, "8.0.0": { "cordova": ">100" diff --git a/plugin.xml b/plugin.xml index c89ac30a..71dc1d6e 100644 --- a/plugin.xml +++ b/plugin.xml @@ -264,18 +264,11 @@ to config.xml in order for the application to find previously stored files. - - - - - - - - - + + - + diff --git a/scripts/electron/expose-node-modules.js b/scripts/electron/expose-node-modules.js deleted file mode 100644 index 55d6f6c2..00000000 --- a/scripts/electron/expose-node-modules.js +++ /dev/null @@ -1,22 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -module.exports = function (context) { - const { projectRoot } = context.opts; - - // preload - const preloadFunctionFileSource = path.resolve(projectRoot+'/plugins/cordova-plugin-file/scripts/electron/preload-function-string.js'); - const preloadFileSourceString = fs.readFileSync(preloadFunctionFileSource, {encoding: 'utf-8'}); - const preloadFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-preload.js'); - let preloadFileDestString = fs.readFileSync(preloadFunctionFileDest, {encoding: 'utf-8'}); - preloadFileDestString += preloadFileSourceString; - fs.writeFileSync(preloadFunctionFileDest, preloadFileDestString, {encoding: 'utf-8'}); - - // main - const mainFunctionFileSource = path.resolve(projectRoot+'/plugins/cordova-plugin-file/scripts/electron/main-function-string.js'); - const mainFileSourceString = fs.readFileSync(mainFunctionFileSource, {encoding: 'utf-8'}); - const mainFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-main.js'); - let mainFileDestString = fs.readFileSync(mainFunctionFileDest, {encoding: 'utf-8'}); - mainFileDestString += mainFileSourceString; - fs.writeFileSync(mainFunctionFileDest, mainFileDestString, {encoding: 'utf-8'}); -} \ No newline at end of file diff --git a/scripts/electron/main-function-string.js b/scripts/electron/main-function-string.js deleted file mode 100644 index 3e5e34ed..00000000 --- a/scripts/electron/main-function-string.js +++ /dev/null @@ -1,15 +0,0 @@ - -/*Beginning of Main function additions for cordova.plugin.file*/ - -ipcMain.on('paths-prefix', (event) => { - const path = require('path'); - event.returnValue = { - applicationDirectory: path.dirname(app.getAppPath()) + path.sep, - dataDirectory: app.getPath('userData') + path.sep, - cacheDirectory: app.getPath('cache') + path.sep, - tempDirectory: app.getPath('temp') + path.sep, - documentsDirectory: app.getPath('documents') + path.sep - }; -}); - -/*End of Main function additions for cordova.plugin.file*/ \ No newline at end of file diff --git a/scripts/electron/preload-function-string.js b/scripts/electron/preload-function-string.js deleted file mode 100644 index 3f5ecfe0..00000000 --- a/scripts/electron/preload-function-string.js +++ /dev/null @@ -1,62 +0,0 @@ - -/*Beginning of Main function additions for cordova.plugin.file*/ -const fs = require('fs'); -contextBridge.exposeInMainWorld('cordovaFilePlugin', { - fs: require('fs'), - path: require('path'), - util: require('util'), - appPaths: ipcRenderer.sendSync('paths-prefix'), - copyFile: (srcPath, destPath, callback) => { - fs.copyFile(srcPath, destPath, callback); - }, - readFile: (path, callback) => { - fs.readdir(path, {withFileTypes: true}, (err, files) => { - if(files && files.length) { - files.forEach(file => { - file.isDirectory = file.isDirectory(); - file.isFile = file.isFile(); - }); - } - callback(err, files); - }); - }, - stat: (path, callback) => { - fs.stat(path, (err, stats) => { - if(stats) { - stats.isDirectory = stats.isDirectory(); - stats.isFile = stats.isFile(); - } - callback(err, stats); - }); - }, - open: (path, flags, callback) => { - fs.open(path, flags, callback); - }, - close: (fd, errorCallback) => { - fs.close(fd, errorCallback); - }, - utimes: (path, atime, mtime, callback) => { - fs.utimes(path, atime, mtime, callback); - }, - rm: (path, callback) => { - fs.rmSync(path, { recursive: true } ,callback); - }, - truncate: (path, size, callback) => { - fs.truncate(path, size, callback); - }, - mkdir: (path, callback) => { - fs.mkdir(path, callback); - }, - write: (fd, data, position, callback) => { - const buffer = Buffer.from(data); - fs.write(fd, buffer, 0, buffer.length, position, callback ); - }, - bufferAlloc: (size) => { - return Buffer.alloc(size); - }, - read: (fd, buffer, position, callback) => { - fs.read(fd, buffer, 0, buffer.length, position, callback) - } -}); - -/*End of Main function additions for cordova.plugin.file*/ \ No newline at end of file diff --git a/scripts/electron/remove-added-code.js b/scripts/electron/remove-added-code.js deleted file mode 100644 index 8da599b7..00000000 --- a/scripts/electron/remove-added-code.js +++ /dev/null @@ -1,33 +0,0 @@ -preloadCommentStartString = '/*Beginning of Main function additions for cordova.plugin.file*/'; -preloadCommentEndString = '/*End of Main function additions for cordova.plugin.file*/'; - -mainCommentStartString = '/*Beginning of Main function additions for cordova.plugin.file*/'; -mainCommentEndString = '/*End of Main function additions for cordova.plugin.file*/'; - -const fs = require('fs'); -const path = require('path'); - - -module.exports = function (context) { - const { projectRoot } = context.opts; - - // preload - const preloadFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-preload.js'); - let fileDestString = fs.readFileSync(preloadFunctionFileDest, {encoding: 'utf-8'}); - const preloadStartIndex = fileDestString.indexOf(preloadCommentStartString); - const preloadEndIndex = fileDestString.indexOf(preloadCommentEndString) + preloadCommentEndString.length; - if(preloadStartIndex > 0) { - fileDestString = fileDestString.substring(0, preloadStartIndex) + fileDestString.substring(preloadEndIndex); - fs.writeFileSync(preloadFunctionFileDest, fileDestString.toString(), {encoding: 'utf-8'}); - } - - // main - const mainFunctionFileDest = path.resolve(projectRoot+'/platforms/electron/platform_www/cdv-electron-main.js'); - let mainFileDestString = fs.readFileSync(mainFunctionFileDest, {encoding: 'utf-8'}); - const mainStartIndex = mainFileDestString.indexOf(mainCommentStartString); - const mainEndIndex = mainFileDestString.indexOf(mainCommentEndString) + mainCommentEndString.length; - if(mainStartIndex > 0) { - mainFileDestString = mainFileDestString.substring(0, mainStartIndex) + mainFileDestString.substring(mainEndIndex); - fs.writeFileSync(mainFunctionFileDest, mainFileDestString.toString(), {encoding: 'utf-8'}); - } -} diff --git a/src/electron/FileProxy.js b/src/electron/FileProxy.js deleted file mode 100644 index 61687102..00000000 --- a/src/electron/FileProxy.js +++ /dev/null @@ -1,504 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -(function () { - /* global require, exports, module */ - - if (window.cordovaFilePlugin === undefined) { - console.error('Something went wrong while installing the plugin. Please remove the plugin and install it again.'); - return; - } - - const nodePath = window.cordovaFilePlugin.path; - - const FileEntry = require('./FileEntry'); - const FileError = require('./FileError'); - const DirectoryEntry = require('./DirectoryEntry'); - const File = require('./File'); - - (function (exports, global) { - const pathsPrefix = window.cordovaFilePlugin.appPaths; - /** * Exported functionality ***/ - - // list a directory's contents (files and folders). - exports.readEntries = function (successCallback, errorCallback, args) { - const fullPath = args[0]; - - if (typeof successCallback !== 'function') { - throw Error('Expected successCallback argument.'); - } - - window.cordovaFilePlugin.readFile(fullPath, (err, files) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - return; - } - const result = []; - files.forEach(d => { - let path = fullPath + d.name; - if (d.isDirectory) { - path += nodePath.sep; - } - result.push({ - isDirectory: d.isDirectory, - isFile: d.isFile, - name: d.name, - fullPath: path, - filesystemName: 'temporary', - nativeURL: path - }); - }); - successCallback(result); - }); - }; - - exports.getFile = function (successCallback, errorCallback, args) { - const path = args[0] + args[1]; - const options = args[2] || {}; - - window.cordovaFilePlugin.stat(path, (err, stats) => { - if (err && err.message && err.message.indexOf('ENOENT') !== 0) { - if (errorCallback) { - errorCallback(FileError.INVALID_STATE_ERR); - } - return; - } - const exists = !err; - const baseName = window.cordovaFilePlugin.path.basename(path); - - function createFile () { - window.cordovaFilePlugin.open(path, 'w', (err, fd) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.INVALID_STATE_ERR); - } - return; - } - window.cordovaFilePlugin.close(fd, (err) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.INVALID_STATE_ERR); - } - return; - } - successCallback(new FileEntry(baseName, path)); - }); - }); - } - - if (options.create === true && options.exclusive === true && exists) { - // If create and exclusive are both true, and the path already exists, - // getFile must fail. - if (errorCallback) { - errorCallback(FileError.PATH_EXISTS_ERR); - } - } else if (options.create === true && !exists) { - // If create is true, the path doesn't exist, and no other error occurs, - // getFile must create it as a zero-length file and return a corresponding - // FileEntry. - createFile(); - } else if (options.create === true && exists) { - if (stats.isFile) { - // Overwrite file, delete then create new. - createFile(); - } else { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } - } else if (!options.create && !exists) { - // If create is not true and the path doesn't exist, getFile must fail. - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - } else if (!options.create && exists && stats.isDirectory) { - // If create is not true and the path exists, but is a directory, getFile - // must fail. - if (errorCallback) { - errorCallback(FileError.TYPE_MISMATCH_ERR); - } - } else { - // Otherwise, if no other error occurs, getFile must return a FileEntry - // corresponding to path. - successCallback(new FileEntry(baseName, path)); - } - }); - }; - - exports.getFileMetadata = function (successCallback, errorCallback, args) { - const fullPath = args[0]; - window.cordovaFilePlugin.stat(fullPath, (err, stats) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - return; - } - const baseName = window.cordovaFilePlugin.path.basename(fullPath); - successCallback(new File(baseName, fullPath, '', stats.mtime, stats.size)); - }); - }; - - exports.getMetadata = function (successCallback, errorCallback, args) { - window.cordovaFilePlugin.stat(args[0], (err, stats) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - return; - } - successCallback({ - modificationTime: stats.mtime, - size: stats.size - }); - }); - }; - - exports.setMetadata = function (successCallback, errorCallback, args) { - const fullPath = args[0]; - const metadataObject = args[1]; - - window.cordovaFilePlugin.utimes(fullPath, metadataObject.modificationTime, metadataObject.modificationTime, (err) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - return; - } - successCallback(); - }); - }; - - exports.write = function (successCallback, errorCallback, args) { - const fileName = args[0]; - const data = args[1]; - const position = args[2]; - const isBinary = args[3]; // eslint-disable-line no-unused-vars - - if (!data) { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - return; - } - - const promisify = window.cordovaFilePlugin.util.promisify; - let bytesWritten = 0; - promisify(window.cordovaFilePlugin.open)(fileName, 'a') - .then(fd => { - return promisify(window.cordovaFilePlugin.write)(fd, data, position) - .then(bw => { bytesWritten = bw; }) - .catch(err => console.log(err)) - .finally(() => promisify(window.cordovaFilePlugin.close)(fd)); - }) - .then(() => successCallback(bytesWritten)) - .catch((err) => { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - }); - }; - - exports.readAsText = function (successCallback, errorCallback, args) { - const fileName = args[0]; - const enc = args[1]; - const startPos = args[2]; - const endPos = args[3]; - - readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsDataURL = function (successCallback, errorCallback, args) { - const fileName = args[0]; - const startPos = args[1]; - const endPos = args[2]; - - readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsBinaryString = function (successCallback, errorCallback, args) { - const fileName = args[0]; - const startPos = args[1]; - const endPos = args[2]; - - readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.readAsArrayBuffer = function (successCallback, errorCallback, args) { - const fileName = args[0]; - const startPos = args[1]; - const endPos = args[2]; - - readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback); - }; - - exports.remove = function (successCallback, errorCallback, args) { - const fullPath = args[0]; - - window.cordovaFilePlugin.stat(fullPath, (err, stats) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - return; - } - window.cordovaFilePlugin.rm(fullPath, (err) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NO_MODIFICATION_ALLOWED_ERR); - } - return; - } - successCallback(); - }); - }); - }; - - exports.truncate = function (successCallback, errorCallback, args) { - const fullPath = args[0]; - const size = args[1]; - - window.cordovaFilePlugin.truncate(fullPath, size, err => { - if (err) { - if (errorCallback) { - errorCallback(FileError.INVALID_STATE_ERR); - } - return; - } - successCallback(size); - }); - }; - - exports.removeRecursively = function (successCallback, errorCallback, args) { - const fullPath = args[0]; - window.cordovaFilePlugin.rm(fullPath, (err) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NO_MODIFICATION_ALLOWED_ERR); - } - return; - } - successCallback(); - }); - }; - - exports.getDirectory = function (successCallback, errorCallback, args) { - const path = args[0] + args[1]; - const options = args[2] || {}; - - window.cordovaFilePlugin.stat(path, (err, stats) => { - if (err && err.message && err.message.indexOf('ENOENT') !== 0) { - if (errorCallback) { - errorCallback(FileError.INVALID_STATE_ERR); - } - return; - } - const exists = !err; - const baseName = window.cordovaFilePlugin.path.basename(path); - - if (options.create === true && options.exclusive === true && exists) { - // If create and exclusive are both true, and the path already exists, - // getDirectory must fail. - if (errorCallback) { - errorCallback(FileError.PATH_EXISTS_ERR); - } - } else if (options.create === true && !exists) { - // If create is true, the path doesn't exist, and no other error occurs, - // getDirectory must create it as a zero-length file and return a corresponding - // MyDirectoryEntry. - window.cordovaFilePlugin.mkdir(path, (err) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.PATH_EXISTS_ERR); - } - return; - } - successCallback(new DirectoryEntry(baseName, path)); - }); - } else if (options.create === true && exists) { - if (stats.isDirectory) { - successCallback(new DirectoryEntry(baseName, path)); - } else if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - } else if (!options.create && !exists) { - // If create is not true and the path doesn't exist, getDirectory must fail. - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - } else if (!options.create && exists && stats.isFile) { - // If create is not true and the path exists, but is a file, getDirectory - // must fail. - if (errorCallback) { - errorCallback(FileError.TYPE_MISMATCH_ERR); - } - } else { - // Otherwise, if no other error occurs, getDirectory must return a - // DirectoryEntry corresponding to path. - successCallback(new DirectoryEntry(baseName, path)); - } - }); - }; - - exports.getParent = function (successCallback, errorCallback, args) { - if (typeof successCallback !== 'function') { - throw Error('Expected successCallback argument.'); - } - - const parentPath = nodePath.dirname(args[0]); - const parentName = nodePath.basename(parentPath); - const path = nodePath.dirname(parentPath) + nodePath.sep; - - exports.getDirectory(successCallback, errorCallback, [path, parentName, {create: false}]); - }; - - exports.copyTo = function (successCallback, errorCallback, args) { - const srcPath = args[0]; - const dstDir = args[1]; - const dstName = args[2]; - - window.cordovaFilePlugin.copyFile(srcPath, dstDir + dstName, (err) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.INVALID_MODIFICATION_ERR); - } - return; - } - exports.getFile(successCallback, errorCallback, [dstDir, dstName]); - }); - }; - - exports.moveTo = function (successCallback, errorCallback, args) { - const srcPath = args[0]; - // parentFullPath and name parameters is ignored because - // args is being passed downstream to exports.copyTo method - const parentFullPath = args[1]; // eslint-disable-line - const name = args[2]; // eslint-disable-line - - exports.copyTo(function (fileEntry) { - - exports.remove(function () { - successCallback(fileEntry); - }, errorCallback, [srcPath]); - - }, errorCallback, args); - }; - - exports.resolveLocalFileSystemURI = function (successCallback, errorCallback, args) { - let path = args[0]; - - // support for encodeURI - if (/\%5/g.test(path) || /\%20/g.test(path)) { // eslint-disable-line no-useless-escape - path = decodeURI(path); - } - - // support for cdvfile - if (path.trim().substr(0, 7) === 'cdvfile') { - if (path.indexOf('cdvfile://localhost') === -1) { - if (errorCallback) { - errorCallback(FileError.ENCODING_ERR); - } - return; - } - - const indexApplication = path.indexOf('application'); - const indexPersistent = path.indexOf('persistent'); - const indexTemporary = path.indexOf('temporary'); - - if (indexApplication !== -1) { // cdvfile://localhost/application/path/to/file - path = pathsPrefix.applicationDirectory + path.substr(indexApplication + 12); - } else if (indexPersistent !== -1) { // cdvfile://localhost/persistent/path/to/file - path = pathsPrefix.dataDirectory + path.substr(indexPersistent + 11); - } else if (indexTemporary !== -1) { // cdvfile://localhost/temporary/path/to/file - path = pathsPrefix.tempDirectory + path.substr(indexTemporary + 10); - } else { - if (errorCallback) { - errorCallback(FileError.ENCODING_ERR); - } - return; - } - } - - window.cordovaFilePlugin.stat(path, (err, stats) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - return; - } - - const baseName = window.cordovaFilePlugin.path.basename(path); - if (stats.isDirectory) { - successCallback(new DirectoryEntry(baseName, path)); - } else { - successCallback(new FileEntry(baseName, path)); - } - }); - }; - - exports.requestAllPaths = function (successCallback) { - successCallback(pathsPrefix); - }; - - /** * Helpers ***/ - - function readAs (what, fullPath, encoding, startPos, endPos, successCallback, errorCallback) { - const promisify = window.cordovaFilePlugin.util.promisify; - - window.cordovaFilePlugin.open(fullPath, 'r', (err, fd) => { - if (err) { - if (errorCallback) { - errorCallback(FileError.NOT_FOUND_ERR); - } - return; - } - const buf = window.cordovaFilePlugin.bufferAlloc(endPos - startPos); - promisify(window.cordovaFilePlugin.read)(fd, buf, startPos) - .then(() => { - switch (what) { - case 'text': - successCallback(buf.toString(encoding)); - break; - case 'dataURL': - successCallback('data:;base64,' + buf.toString('base64')); - break; - case 'arrayBuffer': - successCallback(buf); - break; - case 'binaryString': - successCallback(buf.toString('binary')); - break; - } - }) - .catch(() => { - if (errorCallback) { - errorCallback(FileError.NOT_READABLE_ERR); - } - }) - .then(() => promisify(fs.close)(fd)); - }); - } - - })(module.exports, window); - - require('cordova/exec/proxy').add('File', module.exports); -})(); \ No newline at end of file diff --git a/src/electron/index.js b/src/electron/index.js new file mode 100644 index 00000000..0a350f57 --- /dev/null +++ b/src/electron/index.js @@ -0,0 +1,437 @@ +const fs = require('fs-extra'); +const nodePath = require('path'); +const app = require('electron').app; + +const FileError = require('../../www/FileError'); + +pathsPrefix = { + applicationDirectory: nodePath.dirname(app.getAppPath()) + nodePath.sep, + dataDirectory: app.getPath('userData') + nodePath.sep, + cacheDirectory: app.getPath('cache') + nodePath.sep, + tempDirectory: app.getPath('temp') + nodePath.sep, + documentsDirectory: app.getPath('documents') + nodePath.sep +}; + +function returnEntry(isFile, name, fullPath, filesystem = null, nativeURL = null) { + return { + isFile, + isDirectory: !isFile, + name, + fullPath, + filesystem, + nativeURL + } +} + +module.exports = { + + readEntries: ([args]) => { + const fullPath = args[0]; + return new Promise((resolve, reject) => { + fs.readdir(fullPath, {withFileTypes: true}, (err, files) => { + if (err) { + reject(err); + return; + } + const result = []; + files.forEach(d => { + let path = fullPath + d.name; + if (d.isDirectory()) { + path += nodePath.sep; + } + result.push({ + isDirectory: d.isDirectory(), + isFile: d.isFile(), + name: d.name, + fullPath: path, + filesystemName: 'temporary', + nativeURL: path + }); + }); + resolve(result); + }); + }) + }, + + getFile, + + getFileMetadata: ([args]) => { + const fullPath = args[0]; + return new Promise((resolve, reject) => { + fs.stat(fullPath, (err, stats) => { + if (err) { + reject(FileError.NOT_FOUND_ERR); + return; + } + const baseName = nodePath.basename(fullPath); + resolve({name: baseName, localURL: fullPath, type: '', lastModified: stats.mtime, size: stats.size, lastModifiedDate: stats.mtime}); + }); + }) + }, + + getMetadata: ([args]) => { + return new Promise((resolve, reject) => { + fs.stat(args[0], (err, stats) => { + if (err) { + reject(FileError.NOT_FOUND_ERR); + return; + } + resolve({ + modificationTime: stats.mtime, + size: stats.size + }); + }); + }) + }, + + setMetadata: ([args]) => { + const fullPath = args[0]; + const metadataObject = args[1]; + return new Promise((resolve, reject) => { + fs.utimes(fullPath, metadataObject.modificationTime, metadataObject.modificationTime, (err) => { + if (err) { + reject(FileError.NOT_FOUND_ERR); + return; + } + resolve(); + }); + }) + }, + + readAsText: ([args]) => { + const fileName = args[0]; + const enc = args[1]; + const startPos = args[2]; + const endPos = args[3]; + return readAs('text', fileName, enc, startPos, endPos); + }, + + readAsDataURL: ([args]) => { + const fileName = args[0]; + const startPos = args[1]; + const endPos = args[2]; + + return readAs('dataURL', fileName, null, startPos, endPos); + }, + + readAsBinaryString: ([args]) => { + const fileName = args[0]; + const startPos = args[1]; + const endPos = args[2]; + + return readAs('binaryString', fileName, null, startPos, endPos); + }, + + readAsArrayBuffer: ([args]) => { + const fileName = args[0]; + const startPos = args[1]; + const endPos = args[2]; + + return readAs('arrayBuffer', fileName, null, startPos, endPos); + }, + + remove: ([args]) => { + const fullPath = args[0]; + return new Promise((resolve, reject) => { + fs.stat(fullPath, (err, stats) => { + if (err) { + reject(FileError.NOT_FOUND_ERR); + return; + } + fs.remove(fullPath, (err) => { + if (err) { + reject(FileError.NO_MODIFICATION_ALLOWED_ERR); + return; + } + resolve(); + }); + }); + }) + }, + + removeRecursively: this.remove, + + getDirectory: getDirectory, + + getParent: ([args]) => { + const parentPath = nodePath.dirname(args[0]); + const parentName = nodePath.basename(parentPath); + const path = nodePath.dirname(parentPath) + nodePath.sep; + + return getDirectory([[path, parentName, {create: false}]]); + }, + + copyTo: ([args]) => { + const srcPath = args[0]; + const dstDir = args[1]; + const dstName = args[2]; + return new Promise((resolve, reject) => { + fs.copyFile(srcPath, dstDir + dstName, async (err) => { + if (err) { + reject(FileError.INVALID_MODIFICATION_ERR); + return; + } + resolve(await getFile([[dstDir, dstName]])); + }); + }) + }, + + moveTo: ([args]) => { + const srcPath = args[0]; + // parentFullPath and name parameters is ignored because + // args is being passed downstream to copyTo method + const dstDir = args[1]; // eslint-disable-line + const dstName = args[2]; // eslint-disable-line + return new Promise((resolve, reject) => { + fs.move(srcPath, dstDir + dstName, {overwrite: true}) + .then(async () => { + resolve(await getFile([[dstDir, dstName]])); + }) + .catch(err => reject(err)); + }) + }, + + resolveLocalFileSystemURI: ([args]) => { + let path = args[0]; + + // support for encodeURI + if (/\%5/g.test(path) || /\%20/g.test(path)) { // eslint-disable-line no-useless-escape + path = decodeURI(path); + } + // support for cdvfile + if (path.trim().substr(0, 7) === 'cdvfile') { + if (path.indexOf('cdvfile://localhost') === -1) { + reject(FileError.ENCODING_ERR); + return; + } + + const indexApplication = path.indexOf('application'); + const indexPersistent = path.indexOf('persistent'); + const indexTemporary = path.indexOf('temporary'); + + if (indexApplication !== -1) { // cdvfile://localhost/application/path/to/file + path = pathsPrefix.applicationDirectory + path.substr(indexApplication + 12); + } else if (indexPersistent !== -1) { // cdvfile://localhost/persistent/path/to/file + path = pathsPrefix.dataDirectory + path.substr(indexPersistent + 11); + } else if (indexTemporary !== -1) { // cdvfile://localhost/temporary/path/to/file + path = pathsPrefix.tempDirectory + path.substr(indexTemporary + 10); + } else { + reject(FileError.ENCODING_ERR); + return; + } + } + + return new Promise((resolve, reject) => { + fs.stat(path, (err, stats) => { + if (err) { + reject(new Error(FileError.NOT_FOUND_ERR)); + return; + } + + const baseName = nodePath.basename(path); + if (stats.isDirectory()) { + // add trailing slash if it is missing + if ((path) && !/\/$/.test(path)) { + path += '/'; + } + resolve(returnEntry(false, baseName, path)); + } else { + // remove trailing slash if it is present + if (path && /\/$/.test(path)) { + path = path.substring(0, path.length - 1); + } + resolve(returnEntry(true, baseName, path)); + } + }); + }); + + }, + + requestAllPaths: () => { + return pathsPrefix; + }, + + write: ([args]) => { + const fileName = args[0]; + const data = args[1]; + const position = args[2]; + const isBinary = args[3]; // eslint-disable-line no-unused-vars + return new Promise((resolve, reject) => { + if (!data) { + reject(FileError.INVALID_MODIFICATION_ERR); + return; + } + + const buf = Buffer.from(data); + let bytesWritten = 0; + fs.open(fileName, 'a') + .then(fd => { + return fs.write(fd, buf, 0, buf.length, position) + .then(bw => { bytesWritten = bw; }) + .finally(() => fs.close(fd)); + }) + .then(() => resolve(bytesWritten)) + .catch(() => { + reject(FileError.INVALID_MODIFICATION_ERR); + }); + }); + }, + + truncate: ([args]) => { + const fullPath = args[0]; + const size = args[1]; + return new Promise((resolve, reject) => { + fs.truncate(fullPath, size, err => { + if (err) { + reject(FileError.INVALID_STATE_ERR); + return; + } + resolve(size); + }); + }) + } +}; + +/** * Helpers ***/ + +function readAs(what, fullPath, encoding, startPos, endPos) { + return new Promise((resolve, reject) => { + fs.open(fullPath, 'r', (err, fd) => { + if (err) { + reject(FileError.NOT_FOUND_ERR); + return; + } + const buf = Buffer.alloc(endPos - startPos); + fs.read(fd, buf, 0, buf.length, startPos) + .then(() => { + switch (what) { + case 'text': + resolve(buf.toString(encoding)); + break; + case 'dataURL': + resolve('data:;base64,' + buf.toString('base64')); + break; + case 'arrayBuffer': + resolve(buf); + break; + case 'binaryString': + resolve(buf.toString('binary')); + break; + } + }) + .catch(() => { + reject(FileError.NOT_READABLE_ERR); + }) + .then(() => fs.close(fd)); + }); + }) +} + +function getFile([args]) { + const path = args[0] + args[1]; + const options = args[2] || {}; + return new Promise((resolve, reject) => { + fs.stat(path, (err, stats) => { + if (err && err.message && err.message.indexOf('ENOENT') !== 0) { + reject(FileError.INVALID_STATE_ERR); + return; + } + const exists = !err; + const baseName = nodePath.basename(path); + + function createFile () { + fs.open(path, 'w', (err, fd) => { + if (err) { + reject(FileError.INVALID_STATE_ERR); + return; + } + fs.close(fd, (err) => { + if (err) { + reject(FileError.INVALID_STATE_ERR); + return; + } + resolve(returnEntry(true, baseName, path)); + }); + }); + } + + if (options.create === true && options.exclusive === true && exists) { + // If create and exclusive are both true, and the path already exists, + // getFile must fail. + reject(FileError.PATH_EXISTS_ERR); + } else if (options.create === true && !exists) { + // If create is true, the path doesn't exist, and no other error occurs, + // getFile must create it as a zero-length file and return a corresponding + // FileEntry. + createFile(); + } else if (options.create === true && exists) { + if (stats.isFile()) { + // Overwrite file, delete then create new. + createFile(); + } else { + reject(FileError.INVALID_MODIFICATION_ERR); + } + } else if (!options.create && !exists) { + // If create is not true and the path doesn't exist, getFile must fail. + reject(FileError.NOT_FOUND_ERR); + } else if (!options.create && exists && stats.isDirectory()) { + // If create is not true and the path exists, but is a directory, getFile + // must fail. + reject(FileError.TYPE_MISMATCH_ERR); + } else { + // Otherwise, if no other error occurs, getFile must return a FileEntry + // corresponding to path. + resolve(returnEntry(true, baseName, path)); + } + }); + }); +} + +function getDirectory([args]) { + const path = args[0] + args[1]; + const options = args[2] || {}; + return new Promise((resolve, reject) => { + fs.stat(path, (err, stats) => { + if (err && err.message && err.message.indexOf('ENOENT') !== 0) { + reject(FileError.INVALID_STATE_ERR); + return; + } + const exists = !err; + const baseName = nodePath.basename(path); + + if (options.create === true && options.exclusive === true && exists) { + // If create and exclusive are both true, and the path already exists, + // getDirectory must fail. + reject(FileError.PATH_EXISTS_ERR); + } else if (options.create === true && !exists) { + // If create is true, the path doesn't exist, and no other error occurs, + // getDirectory must create it as a zero-length file and return a corresponding + // MyDirectoryEntry. + fs.mkdir(path, (err) => { + if (err) { + reject(FileError.PATH_EXISTS_ERR); + return; + } + resolve(returnEntry(false, baseName, path)); + }); + } else if (options.create === true && exists) { + if (stats.isDirectory()) { + resolve(returnEntry(false, baseName, path)); + } else { + reject(FileError.INVALID_MODIFICATION_ERR); + } + } else if (!options.create && !exists) { + // If create is not true and the path doesn't exist, getDirectory must fail. + reject(FileError.NOT_FOUND_ERR); + } else if (!options.create && exists && stats.isFile()) { + // If create is not true and the path exists, but is a file, getDirectory + // must fail. + reject(FileError.TYPE_MISMATCH_ERR); + } else { + // Otherwise, if no other error occurs, getDirectory must return a + // DirectoryEntry corresponding to path. + resolve(returnEntry(false, baseName, path)); + } + }); + }) +} \ No newline at end of file diff --git a/src/electron/package.json b/src/electron/package.json new file mode 100644 index 00000000..e6edf05b --- /dev/null +++ b/src/electron/package.json @@ -0,0 +1,20 @@ +{ + "name": "cordova-plugin-file-electron", + "version": "1.0.0", + "description": "Electron Native Support for Cordova File Plugin", + "main": "index.js", + "keywords": [ + "cordova", + "electron", + "file", + "native" + ], + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "dependencies": { + "fs-extra": "^11.1.0" + }, + "cordova": { + "serviceName": "File" + } +} From 2da73995242dec41265d12955389b4e6d976d423 Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:08:03 +0900 Subject: [PATCH 03/32] chore: update package-lock.json w/ cordova requirements --- package-lock.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 06d2ee43..a0d38f38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "cordova-android": ">=6.3.0" }, "7.0.0": { - "cordova-android": ">=10.0.0" + "cordova-android": ">=10.0.0", + "cordova-electron": ">=3.0.0" }, "8.0.0": { "cordova": ">100" From 493ba055848a041950578c8deb6ce0b0c5c9041a Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:08:39 +0900 Subject: [PATCH 04/32] chore: fix lint errors w/ automatic correction --- src/electron/index.js | 63 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 0a350f57..590da253 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -12,7 +12,7 @@ pathsPrefix = { documentsDirectory: app.getPath('documents') + nodePath.sep }; -function returnEntry(isFile, name, fullPath, filesystem = null, nativeURL = null) { +function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = null) { return { isFile, isDirectory: !isFile, @@ -20,15 +20,15 @@ function returnEntry(isFile, name, fullPath, filesystem = null, nativeURL = null fullPath, filesystem, nativeURL - } + }; } module.exports = { readEntries: ([args]) => { const fullPath = args[0]; - return new Promise((resolve, reject) => { - fs.readdir(fullPath, {withFileTypes: true}, (err, files) => { + return new Promise((resolve, reject) => { + fs.readdir(fullPath, { withFileTypes: true }, (err, files) => { if (err) { reject(err); return; @@ -50,7 +50,7 @@ module.exports = { }); resolve(result); }); - }) + }); }, getFile, @@ -64,9 +64,9 @@ module.exports = { return; } const baseName = nodePath.basename(fullPath); - resolve({name: baseName, localURL: fullPath, type: '', lastModified: stats.mtime, size: stats.size, lastModifiedDate: stats.mtime}); + resolve({ name: baseName, localURL: fullPath, type: '', lastModified: stats.mtime, size: stats.size, lastModifiedDate: stats.mtime }); }); - }) + }); }, getMetadata: ([args]) => { @@ -81,7 +81,7 @@ module.exports = { size: stats.size }); }); - }) + }); }, setMetadata: ([args]) => { @@ -95,7 +95,7 @@ module.exports = { } resolve(); }); - }) + }); }, readAsText: ([args]) => { @@ -146,7 +146,7 @@ module.exports = { resolve(); }); }); - }) + }); }, removeRecursively: this.remove, @@ -158,7 +158,7 @@ module.exports = { const parentName = nodePath.basename(parentPath); const path = nodePath.dirname(parentPath) + nodePath.sep; - return getDirectory([[path, parentName, {create: false}]]); + return getDirectory([[path, parentName, { create: false }]]); }, copyTo: ([args]) => { @@ -173,7 +173,7 @@ module.exports = { } resolve(await getFile([[dstDir, dstName]])); }); - }) + }); }, moveTo: ([args]) => { @@ -183,19 +183,19 @@ module.exports = { const dstDir = args[1]; // eslint-disable-line const dstName = args[2]; // eslint-disable-line return new Promise((resolve, reject) => { - fs.move(srcPath, dstDir + dstName, {overwrite: true}) - .then(async () => { - resolve(await getFile([[dstDir, dstName]])); - }) - .catch(err => reject(err)); - }) + fs.move(srcPath, dstDir + dstName, { overwrite: true }) + .then(async () => { + resolve(await getFile([[dstDir, dstName]])); + }) + .catch(err => reject(err)); + }); }, resolveLocalFileSystemURI: ([args]) => { let path = args[0]; // support for encodeURI - if (/\%5/g.test(path) || /\%20/g.test(path)) { // eslint-disable-line no-useless-escape + if (/\%5/g.test(path) || /\%20/g.test(path)) { // eslint-disable-line no-useless-escape path = decodeURI(path); } // support for cdvfile @@ -227,7 +227,7 @@ module.exports = { reject(new Error(FileError.NOT_FOUND_ERR)); return; } - + const baseName = nodePath.basename(path); if (stats.isDirectory()) { // add trailing slash if it is missing @@ -244,7 +244,6 @@ module.exports = { } }); }); - }, requestAllPaths: () => { @@ -261,14 +260,14 @@ module.exports = { reject(FileError.INVALID_MODIFICATION_ERR); return; } - + const buf = Buffer.from(data); let bytesWritten = 0; fs.open(fileName, 'a') .then(fd => { return fs.write(fd, buf, 0, buf.length, position) - .then(bw => { bytesWritten = bw; }) - .finally(() => fs.close(fd)); + .then(bw => { bytesWritten = bw; }) + .finally(() => fs.close(fd)); }) .then(() => resolve(bytesWritten)) .catch(() => { @@ -288,13 +287,13 @@ module.exports = { } resolve(size); }); - }) - } + }); + } }; /** * Helpers ***/ -function readAs(what, fullPath, encoding, startPos, endPos) { +function readAs (what, fullPath, encoding, startPos, endPos) { return new Promise((resolve, reject) => { fs.open(fullPath, 'r', (err, fd) => { if (err) { @@ -324,10 +323,10 @@ function readAs(what, fullPath, encoding, startPos, endPos) { }) .then(() => fs.close(fd)); }); - }) + }); } -function getFile([args]) { +function getFile ([args]) { const path = args[0] + args[1]; const options = args[2] || {}; return new Promise((resolve, reject) => { @@ -387,7 +386,7 @@ function getFile([args]) { }); } -function getDirectory([args]) { +function getDirectory ([args]) { const path = args[0] + args[1]; const options = args[2] || {}; return new Promise((resolve, reject) => { @@ -433,5 +432,5 @@ function getDirectory([args]) { resolve(returnEntry(false, baseName, path)); } }); - }) -} \ No newline at end of file + }); +} From bee2c3f29f40b977f7c7cce843381128e487f4b1 Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:09:52 +0900 Subject: [PATCH 05/32] chore: fix lint error pathsPrefix not defined --- src/electron/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/electron/index.js b/src/electron/index.js index 590da253..ea76b0a1 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -4,7 +4,7 @@ const app = require('electron').app; const FileError = require('../../www/FileError'); -pathsPrefix = { +const pathsPrefix = { applicationDirectory: nodePath.dirname(app.getAppPath()) + nodePath.sep, dataDirectory: app.getPath('userData') + nodePath.sep, cacheDirectory: app.getPath('cache') + nodePath.sep, From 45f338a9b12f1a26cbce29fad3f8623daf3b8fe3 Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:17:24 +0900 Subject: [PATCH 06/32] chore: fix lint error Buffer is not defined --- src/electron/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/electron/index.js b/src/electron/index.js index ea76b0a1..affdf3f0 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -1,3 +1,4 @@ +const { Buffer } = require('node:buffer'); const fs = require('fs-extra'); const nodePath = require('path'); const app = require('electron').app; From 26472451f288d75b23e46761351f45afd54d5eb1 Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:43:05 +0900 Subject: [PATCH 07/32] chore: update path variable names --- src/electron/index.js | 76 +++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index affdf3f0..794b76a4 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -36,17 +36,17 @@ module.exports = { } const result = []; files.forEach(d => { - let path = fullPath + d.name; + let absolutePath = fullPath + d.name; if (d.isDirectory()) { - path += nodePath.sep; + absolutePath += nodePath.sep; } result.push({ isDirectory: d.isDirectory(), isFile: d.isFile(), name: d.name, - fullPath: path, + fullPath: absolutePath, filesystemName: 'temporary', - nativeURL: path + nativeURL: absolutePath }); }); resolve(result); @@ -157,9 +157,9 @@ module.exports = { getParent: ([args]) => { const parentPath = nodePath.dirname(args[0]); const parentName = nodePath.basename(parentPath); - const path = nodePath.dirname(parentPath) + nodePath.sep; + const fullPath = nodePath.dirname(parentPath) + nodePath.sep; - return getDirectory([[path, parentName, { create: false }]]); + return getDirectory([[fullPath, parentName, { create: false }]]); }, copyTo: ([args]) => { @@ -193,29 +193,29 @@ module.exports = { }, resolveLocalFileSystemURI: ([args]) => { - let path = args[0]; + let uri = args[0]; // support for encodeURI - if (/\%5/g.test(path) || /\%20/g.test(path)) { // eslint-disable-line no-useless-escape - path = decodeURI(path); + if (/\%5/g.test(uri) || /\%20/g.test(uri)) { // eslint-disable-line no-useless-escape + uri = decodeURI(uri); } // support for cdvfile - if (path.trim().substr(0, 7) === 'cdvfile') { - if (path.indexOf('cdvfile://localhost') === -1) { + if (uri.trim().substr(0, 7) === 'cdvfile') { + if (uri.indexOf('cdvfile://localhost') === -1) { reject(FileError.ENCODING_ERR); return; } - const indexApplication = path.indexOf('application'); - const indexPersistent = path.indexOf('persistent'); - const indexTemporary = path.indexOf('temporary'); + const indexApplication = uri.indexOf('application'); + const indexPersistent = uri.indexOf('persistent'); + const indexTemporary = uri.indexOf('temporary'); if (indexApplication !== -1) { // cdvfile://localhost/application/path/to/file - path = pathsPrefix.applicationDirectory + path.substr(indexApplication + 12); + uri = pathsPrefix.applicationDirectory + uri.substr(indexApplication + 12); } else if (indexPersistent !== -1) { // cdvfile://localhost/persistent/path/to/file - path = pathsPrefix.dataDirectory + path.substr(indexPersistent + 11); + uri = pathsPrefix.dataDirectory + uri.substr(indexPersistent + 11); } else if (indexTemporary !== -1) { // cdvfile://localhost/temporary/path/to/file - path = pathsPrefix.tempDirectory + path.substr(indexTemporary + 10); + uri = pathsPrefix.tempDirectory + uri.substr(indexTemporary + 10); } else { reject(FileError.ENCODING_ERR); return; @@ -223,25 +223,25 @@ module.exports = { } return new Promise((resolve, reject) => { - fs.stat(path, (err, stats) => { + fs.stat(uri, (err, stats) => { if (err) { reject(new Error(FileError.NOT_FOUND_ERR)); return; } - const baseName = nodePath.basename(path); + const baseName = nodePath.basename(uri); if (stats.isDirectory()) { // add trailing slash if it is missing - if ((path) && !/\/$/.test(path)) { - path += '/'; + if ((uri) && !/\/$/.test(uri)) { + uri += '/'; } - resolve(returnEntry(false, baseName, path)); + resolve(returnEntry(false, baseName, uri)); } else { // remove trailing slash if it is present - if (path && /\/$/.test(path)) { - path = path.substring(0, path.length - 1); + if (uri && /\/$/.test(uri)) { + uri = uri.substring(0, uri.length - 1); } - resolve(returnEntry(true, baseName, path)); + resolve(returnEntry(true, baseName, uri)); } }); }); @@ -328,19 +328,19 @@ function readAs (what, fullPath, encoding, startPos, endPos) { } function getFile ([args]) { - const path = args[0] + args[1]; + const absolutePath = args[0] + args[1]; const options = args[2] || {}; return new Promise((resolve, reject) => { - fs.stat(path, (err, stats) => { + fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { reject(FileError.INVALID_STATE_ERR); return; } const exists = !err; - const baseName = nodePath.basename(path); + const baseName = nodePath.basename(absolutePath); function createFile () { - fs.open(path, 'w', (err, fd) => { + fs.open(absolutePath, 'w', (err, fd) => { if (err) { reject(FileError.INVALID_STATE_ERR); return; @@ -350,7 +350,7 @@ function getFile ([args]) { reject(FileError.INVALID_STATE_ERR); return; } - resolve(returnEntry(true, baseName, path)); + resolve(returnEntry(true, baseName, absolutePath)); }); }); } @@ -381,23 +381,23 @@ function getFile ([args]) { } else { // Otherwise, if no other error occurs, getFile must return a FileEntry // corresponding to path. - resolve(returnEntry(true, baseName, path)); + resolve(returnEntry(true, baseName, absolutePath)); } }); }); } function getDirectory ([args]) { - const path = args[0] + args[1]; + const absolutePath = args[0] + args[1]; const options = args[2] || {}; return new Promise((resolve, reject) => { - fs.stat(path, (err, stats) => { + fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { reject(FileError.INVALID_STATE_ERR); return; } const exists = !err; - const baseName = nodePath.basename(path); + const baseName = nodePath.basename(absolutePath); if (options.create === true && options.exclusive === true && exists) { // If create and exclusive are both true, and the path already exists, @@ -407,16 +407,16 @@ function getDirectory ([args]) { // If create is true, the path doesn't exist, and no other error occurs, // getDirectory must create it as a zero-length file and return a corresponding // MyDirectoryEntry. - fs.mkdir(path, (err) => { + fs.mkdir(absolutePath, (err) => { if (err) { reject(FileError.PATH_EXISTS_ERR); return; } - resolve(returnEntry(false, baseName, path)); + resolve(returnEntry(false, baseName, absolutePath)); }); } else if (options.create === true && exists) { if (stats.isDirectory()) { - resolve(returnEntry(false, baseName, path)); + resolve(returnEntry(false, baseName, absolutePath)); } else { reject(FileError.INVALID_MODIFICATION_ERR); } @@ -430,7 +430,7 @@ function getDirectory ([args]) { } else { // Otherwise, if no other error occurs, getDirectory must return a // DirectoryEntry corresponding to path. - resolve(returnEntry(false, baseName, path)); + resolve(returnEntry(false, baseName, absolutePath)); } }); }); From ec604bc6c62c531065f534c83046cb7414e6fbcd Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:43:39 +0900 Subject: [PATCH 08/32] chore: rename nodePath to path --- src/electron/index.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 794b76a4..f409740d 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -1,16 +1,16 @@ const { Buffer } = require('node:buffer'); +const path = require('node:path'); const fs = require('fs-extra'); -const nodePath = require('path'); const app = require('electron').app; const FileError = require('../../www/FileError'); const pathsPrefix = { - applicationDirectory: nodePath.dirname(app.getAppPath()) + nodePath.sep, - dataDirectory: app.getPath('userData') + nodePath.sep, - cacheDirectory: app.getPath('cache') + nodePath.sep, - tempDirectory: app.getPath('temp') + nodePath.sep, - documentsDirectory: app.getPath('documents') + nodePath.sep + applicationDirectory: path.dirname(app.getAppPath()) + path.sep, + dataDirectory: app.getPath('userData') + path.sep, + cacheDirectory: app.getPath('cache') + path.sep, + tempDirectory: app.getPath('temp') + path.sep, + documentsDirectory: app.getPath('documents') + path.sep }; function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = null) { @@ -38,7 +38,7 @@ module.exports = { files.forEach(d => { let absolutePath = fullPath + d.name; if (d.isDirectory()) { - absolutePath += nodePath.sep; + absolutePath += path.sep; } result.push({ isDirectory: d.isDirectory(), @@ -64,7 +64,7 @@ module.exports = { reject(FileError.NOT_FOUND_ERR); return; } - const baseName = nodePath.basename(fullPath); + const baseName = path.basename(fullPath); resolve({ name: baseName, localURL: fullPath, type: '', lastModified: stats.mtime, size: stats.size, lastModifiedDate: stats.mtime }); }); }); @@ -155,9 +155,9 @@ module.exports = { getDirectory: getDirectory, getParent: ([args]) => { - const parentPath = nodePath.dirname(args[0]); - const parentName = nodePath.basename(parentPath); - const fullPath = nodePath.dirname(parentPath) + nodePath.sep; + const parentPath = path.dirname(args[0]); + const parentName = path.basename(parentPath); + const fullPath = path.dirname(parentPath) + path.sep; return getDirectory([[fullPath, parentName, { create: false }]]); }, @@ -229,7 +229,7 @@ module.exports = { return; } - const baseName = nodePath.basename(uri); + const baseName = path.basename(uri); if (stats.isDirectory()) { // add trailing slash if it is missing if ((uri) && !/\/$/.test(uri)) { @@ -337,7 +337,7 @@ function getFile ([args]) { return; } const exists = !err; - const baseName = nodePath.basename(absolutePath); + const baseName = path.basename(absolutePath); function createFile () { fs.open(absolutePath, 'w', (err, fd) => { @@ -397,7 +397,7 @@ function getDirectory ([args]) { return; } const exists = !err; - const baseName = nodePath.basename(absolutePath); + const baseName = path.basename(absolutePath); if (options.create === true && options.exclusive === true && exists) { // If create and exclusive are both true, and the path already exists, From 7e51bdd3126a5d9e1151c1ed9f015087da00119e Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:44:00 +0900 Subject: [PATCH 09/32] chore: update electron app require block --- src/electron/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/electron/index.js b/src/electron/index.js index f409740d..46fdc260 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -1,7 +1,7 @@ const { Buffer } = require('node:buffer'); const path = require('node:path'); const fs = require('fs-extra'); -const app = require('electron').app; +const { app } = require('electron'); const FileError = require('../../www/FileError'); From b5c2bceecd37394f31e3fbe3d91a5bed8ca83b2c Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:44:38 +0900 Subject: [PATCH 10/32] chore: remove extra new line block --- src/electron/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/electron/index.js b/src/electron/index.js index 46fdc260..b008a706 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -25,7 +25,6 @@ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = nul } module.exports = { - readEntries: ([args]) => { const fullPath = args[0]; return new Promise((resolve, reject) => { From 327378a90b91fbdf1467de14d2452997471c584a Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 18:45:37 +0900 Subject: [PATCH 11/32] chore: convert arrow function to standard function --- src/electron/index.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index b008a706..9b2ef9e0 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -25,7 +25,7 @@ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = nul } module.exports = { - readEntries: ([args]) => { + readEntries: function ([args]) { const fullPath = args[0]; return new Promise((resolve, reject) => { fs.readdir(fullPath, { withFileTypes: true }, (err, files) => { @@ -55,7 +55,7 @@ module.exports = { getFile, - getFileMetadata: ([args]) => { + getFileMetadata: function ([args]) { const fullPath = args[0]; return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { @@ -69,7 +69,7 @@ module.exports = { }); }, - getMetadata: ([args]) => { + getMetadata: function ([args]) { return new Promise((resolve, reject) => { fs.stat(args[0], (err, stats) => { if (err) { @@ -84,7 +84,7 @@ module.exports = { }); }, - setMetadata: ([args]) => { + setMetadata: function ([args]) { const fullPath = args[0]; const metadataObject = args[1]; return new Promise((resolve, reject) => { @@ -98,7 +98,7 @@ module.exports = { }); }, - readAsText: ([args]) => { + readAsText: function ([args]) { const fileName = args[0]; const enc = args[1]; const startPos = args[2]; @@ -106,7 +106,7 @@ module.exports = { return readAs('text', fileName, enc, startPos, endPos); }, - readAsDataURL: ([args]) => { + readAsDataURL: function ([args]) { const fileName = args[0]; const startPos = args[1]; const endPos = args[2]; @@ -114,7 +114,7 @@ module.exports = { return readAs('dataURL', fileName, null, startPos, endPos); }, - readAsBinaryString: ([args]) => { + readAsBinaryString: function ([args]) { const fileName = args[0]; const startPos = args[1]; const endPos = args[2]; @@ -122,7 +122,7 @@ module.exports = { return readAs('binaryString', fileName, null, startPos, endPos); }, - readAsArrayBuffer: ([args]) => { + readAsArrayBuffer: function ([args]) { const fileName = args[0]; const startPos = args[1]; const endPos = args[2]; @@ -130,7 +130,7 @@ module.exports = { return readAs('arrayBuffer', fileName, null, startPos, endPos); }, - remove: ([args]) => { + remove: function ([args]) { const fullPath = args[0]; return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { @@ -153,7 +153,7 @@ module.exports = { getDirectory: getDirectory, - getParent: ([args]) => { + getParent: function ([args]) { const parentPath = path.dirname(args[0]); const parentName = path.basename(parentPath); const fullPath = path.dirname(parentPath) + path.sep; @@ -161,7 +161,7 @@ module.exports = { return getDirectory([[fullPath, parentName, { create: false }]]); }, - copyTo: ([args]) => { + copyTo: function ([args]) { const srcPath = args[0]; const dstDir = args[1]; const dstName = args[2]; @@ -176,7 +176,7 @@ module.exports = { }); }, - moveTo: ([args]) => { + moveTo: function ([args]) { const srcPath = args[0]; // parentFullPath and name parameters is ignored because // args is being passed downstream to copyTo method @@ -191,7 +191,7 @@ module.exports = { }); }, - resolveLocalFileSystemURI: ([args]) => { + resolveLocalFileSystemURI: function ([args]) { let uri = args[0]; // support for encodeURI @@ -250,7 +250,7 @@ module.exports = { return pathsPrefix; }, - write: ([args]) => { + write: function ([args]) { const fileName = args[0]; const data = args[1]; const position = args[2]; @@ -276,7 +276,7 @@ module.exports = { }); }, - truncate: ([args]) => { + truncate: function ([args]) { const fullPath = args[0]; const size = args[1]; return new Promise((resolve, reject) => { From 6d1724136c793090c8996815e744e2cbfb862e6c Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 21:20:22 +0900 Subject: [PATCH 12/32] chore: have argument parameter defined in passed in method parameter when possible --- src/electron/index.js | 80 ++++++++++++------------------------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 9b2ef9e0..39cfd1ca 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -25,8 +25,7 @@ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = nul } module.exports = { - readEntries: function ([args]) { - const fullPath = args[0]; + readEntries: function ([[fullPath]]) { return new Promise((resolve, reject) => { fs.readdir(fullPath, { withFileTypes: true }, (err, files) => { if (err) { @@ -55,8 +54,7 @@ module.exports = { getFile, - getFileMetadata: function ([args]) { - const fullPath = args[0]; + getFileMetadata: function ([[fullPath]]) { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { @@ -69,9 +67,9 @@ module.exports = { }); }, - getMetadata: function ([args]) { + getMetadata: function ([[url]]) { return new Promise((resolve, reject) => { - fs.stat(args[0], (err, stats) => { + fs.stat(url, (err, stats) => { if (err) { reject(FileError.NOT_FOUND_ERR); return; @@ -84,9 +82,7 @@ module.exports = { }); }, - setMetadata: function ([args]) { - const fullPath = args[0]; - const metadataObject = args[1]; + setMetadata: function ([[fullPath, metadataObject]]) { return new Promise((resolve, reject) => { fs.utimes(fullPath, metadataObject.modificationTime, metadataObject.modificationTime, (err) => { if (err) { @@ -98,40 +94,23 @@ module.exports = { }); }, - readAsText: function ([args]) { - const fileName = args[0]; - const enc = args[1]; - const startPos = args[2]; - const endPos = args[3]; + readAsText: function ([[fileName, enc, startPos, endPos]]) { return readAs('text', fileName, enc, startPos, endPos); }, - readAsDataURL: function ([args]) { - const fileName = args[0]; - const startPos = args[1]; - const endPos = args[2]; - + readAsDataURL: function ([[fileName, startPos, endPos]]) { return readAs('dataURL', fileName, null, startPos, endPos); }, - readAsBinaryString: function ([args]) { - const fileName = args[0]; - const startPos = args[1]; - const endPos = args[2]; - + readAsBinaryString: function ([[fileName, startPos, endPos]]) { return readAs('binaryString', fileName, null, startPos, endPos); }, - readAsArrayBuffer: function ([args]) { - const fileName = args[0]; - const startPos = args[1]; - const endPos = args[2]; - + readAsArrayBuffer: function ([[fileName, startPos, endPos]]) { return readAs('arrayBuffer', fileName, null, startPos, endPos); }, - remove: function ([args]) { - const fullPath = args[0]; + remove: function ([[fullPath]]) { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { @@ -153,18 +132,15 @@ module.exports = { getDirectory: getDirectory, - getParent: function ([args]) { - const parentPath = path.dirname(args[0]); + getParent: function ([[url]]) { + const parentPath = path.dirname(url); const parentName = path.basename(parentPath); const fullPath = path.dirname(parentPath) + path.sep; - return getDirectory([[fullPath, parentName, { create: false }]]); + return getDirectory([fullPath, parentName, { create: false }]); }, - copyTo: function ([args]) { - const srcPath = args[0]; - const dstDir = args[1]; - const dstName = args[2]; + copyTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { fs.copyFile(srcPath, dstDir + dstName, async (err) => { if (err) { @@ -176,12 +152,7 @@ module.exports = { }); }, - moveTo: function ([args]) { - const srcPath = args[0]; - // parentFullPath and name parameters is ignored because - // args is being passed downstream to copyTo method - const dstDir = args[1]; // eslint-disable-line - const dstName = args[2]; // eslint-disable-line + moveTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { fs.move(srcPath, dstDir + dstName, { overwrite: true }) .then(async () => { @@ -250,11 +221,8 @@ module.exports = { return pathsPrefix; }, - write: function ([args]) { - const fileName = args[0]; - const data = args[1]; - const position = args[2]; - const isBinary = args[3]; // eslint-disable-line no-unused-vars + write: function ([[fileName, data, position]]) { + // const isBinary[3]; return new Promise((resolve, reject) => { if (!data) { reject(FileError.INVALID_MODIFICATION_ERR); @@ -276,9 +244,7 @@ module.exports = { }); }, - truncate: function ([args]) { - const fullPath = args[0]; - const size = args[1]; + truncate: function ([[fullPath, size]]) { return new Promise((resolve, reject) => { fs.truncate(fullPath, size, err => { if (err) { @@ -326,9 +292,8 @@ function readAs (what, fullPath, encoding, startPos, endPos) { }); } -function getFile ([args]) { - const absolutePath = args[0] + args[1]; - const options = args[2] || {}; +function getFile ([[dstDir, dstName, options = {}]]) { + const absolutePath = dstDir + dstName; return new Promise((resolve, reject) => { fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { @@ -386,9 +351,8 @@ function getFile ([args]) { }); } -function getDirectory ([args]) { - const absolutePath = args[0] + args[1]; - const options = args[2] || {}; +function getDirectory ([[dstDir, dstName, options = {}]]) { + const absolutePath = dstDir + dstName; return new Promise((resolve, reject) => { fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { From 9e1d8a1efd077f1d8bb30c07a80b433a3a589476 Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 21:51:24 +0900 Subject: [PATCH 13/32] chore: add missing apache license header --- src/electron/index.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/electron/index.js b/src/electron/index.js index 39cfd1ca..84624882 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -1,3 +1,24 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + const { Buffer } = require('node:buffer'); const path = require('node:path'); const fs = require('fs-extra'); From bb460986deccfb14c44fc2c1f6ff4de5470d291a Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 22:02:56 +0900 Subject: [PATCH 14/32] chore: various formatting and fix reject is not defined --- src/electron/index.js | 89 ++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 84624882..11f945a8 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -53,12 +53,16 @@ module.exports = { reject(err); return; } + const result = []; + files.forEach(d => { let absolutePath = fullPath + d.name; + if (d.isDirectory()) { absolutePath += path.sep; } + result.push({ isDirectory: d.isDirectory(), isFile: d.isFile(), @@ -68,6 +72,7 @@ module.exports = { nativeURL: absolutePath }); }); + resolve(result); }); }); @@ -82,8 +87,15 @@ module.exports = { reject(FileError.NOT_FOUND_ERR); return; } - const baseName = path.basename(fullPath); - resolve({ name: baseName, localURL: fullPath, type: '', lastModified: stats.mtime, size: stats.size, lastModifiedDate: stats.mtime }); + + resolve({ + name: path.basename(fullPath), + localURL: fullPath, + type: '', + lastModified: stats.mtime, + size: stats.size, + lastModifiedDate: stats.mtime + }); }); }); }, @@ -95,6 +107,7 @@ module.exports = { reject(FileError.NOT_FOUND_ERR); return; } + resolve({ modificationTime: stats.mtime, size: stats.size @@ -105,13 +118,16 @@ module.exports = { setMetadata: function ([[fullPath, metadataObject]]) { return new Promise((resolve, reject) => { - fs.utimes(fullPath, metadataObject.modificationTime, metadataObject.modificationTime, (err) => { + const modificationTime = metadataObject.modificationTime; + const utimesError = function (err) { if (err) { reject(FileError.NOT_FOUND_ERR); return; } resolve(); - }); + }; + + fs.utimes(fullPath, modificationTime, modificationTime, utimesError); }); }, @@ -138,6 +154,7 @@ module.exports = { reject(FileError.NOT_FOUND_ERR); return; } + fs.remove(fullPath, (err) => { if (err) { reject(FileError.NO_MODIFICATION_ALLOWED_ERR); @@ -168,6 +185,7 @@ module.exports = { reject(FileError.INVALID_MODIFICATION_ERR); return; } + resolve(await getFile([[dstDir, dstName]])); }); }); @@ -184,36 +202,37 @@ module.exports = { }, resolveLocalFileSystemURI: function ([args]) { - let uri = args[0]; - - // support for encodeURI - if (/\%5/g.test(uri) || /\%20/g.test(uri)) { // eslint-disable-line no-useless-escape - uri = decodeURI(uri); - } - // support for cdvfile - if (uri.trim().substr(0, 7) === 'cdvfile') { - if (uri.indexOf('cdvfile://localhost') === -1) { - reject(FileError.ENCODING_ERR); - return; + return new Promise((resolve, reject) => { + let uri = args[0]; + + // support for encodeURI + if (/\%5/g.test(uri) || /\%20/g.test(uri)) { // eslint-disable-line no-useless-escape + uri = decodeURI(uri); } - const indexApplication = uri.indexOf('application'); - const indexPersistent = uri.indexOf('persistent'); - const indexTemporary = uri.indexOf('temporary'); + // support for cdvfile + if (uri.trim().substr(0, 7) === 'cdvfile') { + if (uri.indexOf('cdvfile://localhost') === -1) { + reject(FileError.ENCODING_ERR); + return; + } + + const indexApplication = uri.indexOf('application'); + const indexPersistent = uri.indexOf('persistent'); + const indexTemporary = uri.indexOf('temporary'); - if (indexApplication !== -1) { // cdvfile://localhost/application/path/to/file - uri = pathsPrefix.applicationDirectory + uri.substr(indexApplication + 12); - } else if (indexPersistent !== -1) { // cdvfile://localhost/persistent/path/to/file - uri = pathsPrefix.dataDirectory + uri.substr(indexPersistent + 11); - } else if (indexTemporary !== -1) { // cdvfile://localhost/temporary/path/to/file - uri = pathsPrefix.tempDirectory + uri.substr(indexTemporary + 10); - } else { - reject(FileError.ENCODING_ERR); - return; + if (indexApplication !== -1) { // cdvfile://localhost/application/path/to/file + uri = pathsPrefix.applicationDirectory + uri.substr(indexApplication + 12); + } else if (indexPersistent !== -1) { // cdvfile://localhost/persistent/path/to/file + uri = pathsPrefix.dataDirectory + uri.substr(indexPersistent + 11); + } else if (indexTemporary !== -1) { // cdvfile://localhost/temporary/path/to/file + uri = pathsPrefix.tempDirectory + uri.substr(indexTemporary + 10); + } else { + reject(FileError.ENCODING_ERR); + return; + } } - } - return new Promise((resolve, reject) => { fs.stat(uri, (err, stats) => { if (err) { reject(new Error(FileError.NOT_FOUND_ERR)); @@ -226,24 +245,25 @@ module.exports = { if ((uri) && !/\/$/.test(uri)) { uri += '/'; } + resolve(returnEntry(false, baseName, uri)); } else { // remove trailing slash if it is present if (uri && /\/$/.test(uri)) { uri = uri.substring(0, uri.length - 1); } + resolve(returnEntry(true, baseName, uri)); } }); }); }, - requestAllPaths: () => { + requestAllPaths: function () { return pathsPrefix; }, write: function ([[fileName, data, position]]) { - // const isBinary[3]; return new Promise((resolve, reject) => { if (!data) { reject(FileError.INVALID_MODIFICATION_ERR); @@ -252,6 +272,7 @@ module.exports = { const buf = Buffer.from(data); let bytesWritten = 0; + fs.open(fileName, 'a') .then(fd => { return fs.write(fd, buf, 0, buf.length, position) @@ -272,6 +293,7 @@ module.exports = { reject(FileError.INVALID_STATE_ERR); return; } + resolve(size); }); }); @@ -287,7 +309,9 @@ function readAs (what, fullPath, encoding, startPos, endPos) { reject(FileError.NOT_FOUND_ERR); return; } + const buf = Buffer.alloc(endPos - startPos); + fs.read(fd, buf, 0, buf.length, startPos) .then(() => { switch (what) { @@ -321,6 +345,7 @@ function getFile ([[dstDir, dstName, options = {}]]) { reject(FileError.INVALID_STATE_ERR); return; } + const exists = !err; const baseName = path.basename(absolutePath); @@ -330,6 +355,7 @@ function getFile ([[dstDir, dstName, options = {}]]) { reject(FileError.INVALID_STATE_ERR); return; } + fs.close(fd, (err) => { if (err) { reject(FileError.INVALID_STATE_ERR); @@ -380,6 +406,7 @@ function getDirectory ([[dstDir, dstName, options = {}]]) { reject(FileError.INVALID_STATE_ERR); return; } + const exists = !err; const baseName = path.basename(absolutePath); From 84104f08797eb10a5b044256c14bc45826d7675b Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 23 Dec 2022 22:39:28 +0900 Subject: [PATCH 15/32] chore: prepare comment blocks to document each method --- src/electron/index.js | 131 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/src/electron/index.js b/src/electron/index.js index 11f945a8..cbb5537f 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -46,6 +46,12 @@ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = nul } module.exports = { + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ readEntries: function ([[fullPath]]) { return new Promise((resolve, reject) => { fs.readdir(fullPath, { withFileTypes: true }, (err, files) => { @@ -78,8 +84,20 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ getFile, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ getFileMetadata: function ([[fullPath]]) { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { @@ -100,6 +118,12 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ getMetadata: function ([[url]]) { return new Promise((resolve, reject) => { fs.stat(url, (err, stats) => { @@ -116,6 +140,12 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ setMetadata: function ([[fullPath, metadataObject]]) { return new Promise((resolve, reject) => { const modificationTime = metadataObject.modificationTime; @@ -131,22 +161,52 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ readAsText: function ([[fileName, enc, startPos, endPos]]) { return readAs('text', fileName, enc, startPos, endPos); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ readAsDataURL: function ([[fileName, startPos, endPos]]) { return readAs('dataURL', fileName, null, startPos, endPos); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ readAsBinaryString: function ([[fileName, startPos, endPos]]) { return readAs('binaryString', fileName, null, startPos, endPos); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ readAsArrayBuffer: function ([[fileName, startPos, endPos]]) { return readAs('arrayBuffer', fileName, null, startPos, endPos); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ remove: function ([[fullPath]]) { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { @@ -166,10 +226,28 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ removeRecursively: this.remove, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ getDirectory: getDirectory, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ getParent: function ([[url]]) { const parentPath = path.dirname(url); const parentName = path.basename(parentPath); @@ -178,6 +256,12 @@ module.exports = { return getDirectory([fullPath, parentName, { create: false }]); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ copyTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { fs.copyFile(srcPath, dstDir + dstName, async (err) => { @@ -191,6 +275,12 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ moveTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { fs.move(srcPath, dstDir + dstName, { overwrite: true }) @@ -201,6 +291,12 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ resolveLocalFileSystemURI: function ([args]) { return new Promise((resolve, reject) => { let uri = args[0]; @@ -259,10 +355,21 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @returns {Promise} + */ requestAllPaths: function () { return pathsPrefix; }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ write: function ([[fileName, data, position]]) { return new Promise((resolve, reject) => { if (!data) { @@ -286,6 +393,12 @@ module.exports = { }); }, + /** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ truncate: function ([[fullPath, size]]) { return new Promise((resolve, reject) => { fs.truncate(fullPath, size, err => { @@ -302,6 +415,12 @@ module.exports = { /** * Helpers ***/ +/** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ function readAs (what, fullPath, encoding, startPos, endPos) { return new Promise((resolve, reject) => { fs.open(fullPath, 'r', (err, fd) => { @@ -337,6 +456,12 @@ function readAs (what, fullPath, encoding, startPos, endPos) { }); } +/** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ function getFile ([[dstDir, dstName, options = {}]]) { const absolutePath = dstDir + dstName; return new Promise((resolve, reject) => { @@ -398,6 +523,12 @@ function getFile ([[dstDir, dstName, options = {}]]) { }); } +/** + * @todo write comment & param information + * + * @param {Array} param0 + * @returns {Promise} + */ function getDirectory ([[dstDir, dstName, options = {}]]) { const absolutePath = dstDir + dstName; return new Promise((resolve, reject) => { From c02a6a62ce4f0ddc5db835f73c38d4568ad26a91 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Tue, 27 Dec 2022 11:26:28 +0545 Subject: [PATCH 16/32] Simple lint fix --- plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.xml b/plugin.xml index 71dc1d6e..86f6c602 100644 --- a/plugin.xml +++ b/plugin.xml @@ -268,7 +268,7 @@ to config.xml in order for the application to find previously stored files. - + From 70025d315bc87bfdf32a05bf1a3762ec7669f8c5 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Tue, 27 Dec 2022 11:46:26 +0545 Subject: [PATCH 17/32] Added documentation for functions --- src/electron/index.js | 210 ++++++++++++++++++++++++++++-------------- 1 file changed, 141 insertions(+), 69 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index cbb5537f..9478f0bb 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -19,8 +19,8 @@ * */ -const { Buffer } = require('node:buffer'); -const path = require('node:path'); +const { Buffer } = require('buffer'); +const path = require('path'); const fs = require('fs-extra'); const { app } = require('electron'); @@ -34,6 +34,15 @@ const pathsPrefix = { documentsDirectory: app.getPath('documents') + path.sep }; +/** + * Returns an an object that's converted by cordova to a FileEntry or a DirectoryEntry. + * @param {boolean} isFile - is the object a file or a directory. true for file and false for directory. + * @param {String} name - the name of the file. + * @param {String} fullPath - the full path to the file. + * @param {String} [filesystem = null] - the filesystem. + * @param {String} [nativeURL = null] - the native URL of to the file. + * @returns {Promise} - An object containing Entry information. +*/ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = null) { return { isFile, @@ -47,10 +56,12 @@ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = nul module.exports = { /** - * @todo write comment & param information + * Read the file contents as text + * + * @param {[fullPath: String]} params + * fullPath - the full path of the directory to read entries from + * @returns {Promise} - An array of Entries in that directory * - * @param {Array} param0 - * @returns {Promise} */ readEntries: function ([[fullPath]]) { return new Promise((resolve, reject) => { @@ -85,18 +96,23 @@ module.exports = { }, /** - * @todo write comment & param information + * Get the file given the path and fileName. * - * @param {Array} param0 - * @returns {Promise} + * @param {[dstDir: String, dstName: String, options: Object]} param + * dstDir: The fullPath to the directory the file is in. + * dstName: The filename including the extension. + * options: fileOptions {create: boolean, exclusive: boolean}. + * + * @returns {Promise} - The file object that is converted to FileEntry by cordova. */ getFile, /** - * @todo write comment & param information + * get the file Metadata. * - * @param {Array} param0 - * @returns {Promise} + * @param {[fullPath: String]} param + * fullPath: the full path of the file including the extension. + * @returns {Promise} - An Object containing the file metadata. */ getFileMetadata: function ([[fullPath]]) { return new Promise((resolve, reject) => { @@ -119,10 +135,11 @@ module.exports = { }, /** - * @todo write comment & param information + * get the file or directory Metadata. * - * @param {Array} param0 - * @returns {Promise} + * @param {[fullPath: String]} param + * fullPath: the full path of the file or directory. + * @returns {Promise} - An Object containing the metadata. */ getMetadata: function ([[url]]) { return new Promise((resolve, reject) => { @@ -141,10 +158,12 @@ module.exports = { }, /** - * @todo write comment & param information + * set the file or directory Metadata. * - * @param {Array} param0 - * @returns {Promise} + * @param {[fullPath: String, metadataObject: Object]} param + * fullPath: the full path of the file including the extension. + * metadataObject: the object containing metadataValues (currently only supports modificationTime) + * @returns {Promise} - An Object containing the file metadata. */ setMetadata: function ([[fullPath, metadataObject]]) { return new Promise((resolve, reject) => { @@ -162,50 +181,69 @@ module.exports = { }, /** - * @todo write comment & param information + * Read the file contents as text * - * @param {Array} param0 - * @returns {Promise} + * @param {[fileName: String, enc: String, startPos: number, endPos: number]} param + * fileName: The fullPath of the file to be read. + * enc: The encoding to use to read the file. + * startPos: The start position from which to begin reading the file. + * endPos: The end position at which to stop reading the file. + * + * @returns {Promise} The string value within the file. */ readAsText: function ([[fileName, enc, startPos, endPos]]) { return readAs('text', fileName, enc, startPos, endPos); }, /** - * @todo write comment & param information + * Read the file as a data URL. * - * @param {Array} param0 - * @returns {Promise} + * @param {[fileName: String, startPos: number, endPos: number]} param + * fileName: The fullPath of the file to be read. + * startPos: The start position from which to begin reading the file. + * endPos: The end position at which to stop reading the file. + * + * @returns {Promise} the file as a dataUrl. */ readAsDataURL: function ([[fileName, startPos, endPos]]) { return readAs('dataURL', fileName, null, startPos, endPos); }, /** - * @todo write comment & param information + * Read the file contents as binary string. * - * @param {Array} param0 - * @returns {Promise} + * @param {[fileName: String, startPos: number, endPos: number]} param + * fileName: The fullPath of the file to be read. + * startPos: The start position from which to begin reading the file. + * endPos: The end position at which to stop reading the file. + * + * @returns {Promise} The file as a binary string. */ readAsBinaryString: function ([[fileName, startPos, endPos]]) { return readAs('binaryString', fileName, null, startPos, endPos); }, /** - * @todo write comment & param information + * Read the file contents as text * - * @param {Array} param0 - * @returns {Promise} + * @param {[fileName: String, startPos: number, endPos: number]} param + * fileName: The fullPath of the file to be read. + * startPos: The start position from which to begin reading the file. + * endPos: The end position at which to stop reading the file. + * + * @returns {Promise} The file as an arrayBuffer. */ readAsArrayBuffer: function ([[fileName, startPos, endPos]]) { return readAs('arrayBuffer', fileName, null, startPos, endPos); }, /** - * @todo write comment & param information + * Remove the file or directory * - * @param {Array} param0 - * @returns {Promise} + * @param {[fullPath: String]} param + * fullePath: The fullPath of the file or directory. + * + * @returns {Promise} resolves when file or directory is deleted. */ remove: function ([[fullPath]]) { return new Promise((resolve, reject) => { @@ -227,26 +265,34 @@ module.exports = { }, /** - * @todo write comment & param information + * Remove the file or directory * - * @param {Array} param0 - * @returns {Promise} + * @param {[fullPath: String]} param + * fullePath: The fullPath of the file or directory. + * + * @returns {Promise} resolves when file or directory is deleted. */ removeRecursively: this.remove, /** - * @todo write comment & param information + * Get the directory given the path and directory name. * - * @param {Array} param0 - * @returns {Promise} + * @param {[dstDir: String, dstName: String, options: Object]} param + * dstDir: The fullPath to the directory the directory is in. + * dstName: The name of the directory. + * options: options {create: boolean, exclusive: boolean}. + * + * @returns {Promise} The directory object that is converted to DirectoryEntry by cordova. */ getDirectory: getDirectory, /** - * @todo write comment & param information + * Get the Parent directory * - * @param {Array} param0 - * @returns {Promise} + * @param {[url: String]} param + * url: The fullPath to the directory the directory is in. + * + * @returns {Promise} The parent directory object that is converted to DirectoryEntry by cordova. */ getParent: function ([[url]]) { const parentPath = path.dirname(url); @@ -257,10 +303,14 @@ module.exports = { }, /** - * @todo write comment & param information + * Copy File * - * @param {Array} param0 - * @returns {Promise} + * @param {[srcPath: String, dstDir: String, dstName: String]} param + * srcPath: The fullPath to the file including extension. + * dstDir: The destination directory. + * dstName: The destination file name. + * + * @returns {Promise} The copied file. */ copyTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { @@ -276,10 +326,14 @@ module.exports = { }, /** - * @todo write comment & param information + * Move File. Always Overwrites. * - * @param {Array} param0 - * @returns {Promise} + * @param {[srcPath: String, dstDir: String, dstName: String]} param + * srcPath: The fullPath to the file including extension. + * dstDir: The destination directory. + * dstName: The destination file name. + * + * @returns {Promise} The moved file. */ moveTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { @@ -292,15 +346,14 @@ module.exports = { }, /** - * @todo write comment & param information + * resolve the File system URL as a FileEntry or a DirectoryEntry. * - * @param {Array} param0 - * @returns {Promise} + * @param {[uri: String]} param + * uri: The full path for the file. + * @returns {Promise} The entry for the file or directory. */ - resolveLocalFileSystemURI: function ([args]) { + resolveLocalFileSystemURI: function ([[uri]]) { return new Promise((resolve, reject) => { - let uri = args[0]; - // support for encodeURI if (/\%5/g.test(uri) || /\%20/g.test(uri)) { // eslint-disable-line no-useless-escape uri = decodeURI(uri); @@ -356,19 +409,22 @@ module.exports = { }, /** - * @todo write comment & param information + * Gets all the path URLs. * - * @returns {Promise} + * @returns {Object} returns an object with all the paths. */ requestAllPaths: function () { return pathsPrefix; }, /** - * @todo write comment & param information + * Write to a file. * - * @param {Array} param0 - * @returns {Promise} + * @param {[fileName: String, data: String, position: Number]} param + * fileName: the full path of the file including fileName and extension. + * data: the data to be written to the file. + * position: the position offset to start writing from. + * @returns {Promise} An object with information about the amount of bytes written. */ write: function ([[fileName, data, position]]) { return new Promise((resolve, reject) => { @@ -394,9 +450,11 @@ module.exports = { }, /** - * @todo write comment & param information + * Truncate the file. * - * @param {Array} param0 + * @param {[fullPath: String, size: Number]} param + * fullPath: the full path of the file including file extension + * size: the length of the file to truncate to. * @returns {Promise} */ truncate: function ([[fullPath, size]]) { @@ -416,10 +474,16 @@ module.exports = { /** * Helpers ***/ /** - * @todo write comment & param information + * Read the file contents as specified. + * + * @param {[what: String, fileName: String, enc: String, startPos: number, endPos: number]} param + * what: what to read the file as. accepts 'text', 'dataURL', 'arrayBuffer' and 'binaryString' + * fileName: The fullPath of the file to be read. + * enc: The encoding to use to read the file. + * startPos: The start position from which to begin reading the file. + * endPos: The end position at which to stop reading the file. * - * @param {Array} param0 - * @returns {Promise} + * @returns {Promise} The string value within the file. */ function readAs (what, fullPath, encoding, startPos, endPos) { return new Promise((resolve, reject) => { @@ -457,10 +521,14 @@ function readAs (what, fullPath, encoding, startPos, endPos) { } /** - * @todo write comment & param information + * Get the file given the path and fileName. * - * @param {Array} param0 - * @returns {Promise} + * @param {[dstDir: String, dstName: String, options: Object]} param + * dstDir: The fullPath to the directory the file is in. + * dstName: The filename including the extension. + * options: fileOptions {create: boolean, exclusive: boolean}. + * + * @returns {Promise} The file object that is converted to FileEntry by cordova. */ function getFile ([[dstDir, dstName, options = {}]]) { const absolutePath = dstDir + dstName; @@ -524,10 +592,14 @@ function getFile ([[dstDir, dstName, options = {}]]) { } /** - * @todo write comment & param information + * Get the directory given the path and directory name. + * + * @param {[dstDir: String, dstName: String, options: Object]} param + * dstDir: The fullPath to the directory the directory is in. + * dstName: The name of the directory. + * options: options {create: boolean, exclusive: boolean}. * - * @param {Array} param0 - * @returns {Promise} + * @returns {Promise} The directory object that is converted to DirectoryEntry by cordova. */ function getDirectory ([[dstDir, dstName, options = {}]]) { const absolutePath = dstDir + dstName; From 11ba47c0339ae463471803a6d547634b451639f4 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Tue, 27 Dec 2022 11:48:53 +0545 Subject: [PATCH 18/32] lint fixes --- src/electron/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 9478f0bb..85d20edb 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -37,10 +37,10 @@ const pathsPrefix = { /** * Returns an an object that's converted by cordova to a FileEntry or a DirectoryEntry. * @param {boolean} isFile - is the object a file or a directory. true for file and false for directory. - * @param {String} name - the name of the file. + * @param {String} name - the name of the file. * @param {String} fullPath - the full path to the file. * @param {String} [filesystem = null] - the filesystem. - * @param {String} [nativeURL = null] - the native URL of to the file. + * @param {String} [nativeURL = null] - the native URL of to the file. * @returns {Promise} - An object containing Entry information. */ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = null) { From 91776d871cf6d52e5ce01b207c1d67f0f4745d26 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Tue, 27 Dec 2022 17:20:46 +0545 Subject: [PATCH 19/32] Added new line to gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0c318211..e24314c5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ Thumbs.db node_modules src/electron/node_modules -src/electron/package-lock.json \ No newline at end of file +src/electron/package-lock.json From c6fe360beb70f85b2bf44aa57f19fe19abaf49f6 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Wed, 28 Dec 2022 11:35:34 +0545 Subject: [PATCH 20/32] Removed the require FileError since it's already in the global scope --- src/electron/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 85d20edb..0a9f0865 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -24,8 +24,6 @@ const path = require('path'); const fs = require('fs-extra'); const { app } = require('electron'); -const FileError = require('../../www/FileError'); - const pathsPrefix = { applicationDirectory: path.dirname(app.getAppPath()) + path.sep, dataDirectory: app.getPath('userData') + path.sep, From 3fdc4748c826d8de94a290fd59aeaef32fd55170 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Wed, 28 Dec 2022 21:17:03 +0530 Subject: [PATCH 21/32] Re-added the FileError Object --- src/electron/index.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/electron/index.js b/src/electron/index.js index 0a9f0865..1b54eb50 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -32,6 +32,25 @@ const pathsPrefix = { documentsDirectory: app.getPath('documents') + path.sep }; +const FileError = { + // Found in DOMException + NOT_FOUND_ERR: 1, + SECURITY_ERR: 2, + ABORT_ERR: 3, + + // Added by File API specification + NOT_READABLE_ERR: 4, + ENCODING_ERR: 5, + NO_MODIFICATION_ALLOWED_ERR: 6, + INVALID_STATE_ERR: 7, + SYNTAX_ERR: 8, + INVALID_MODIFICATION_ERR: 9, + QUOTA_EXCEEDED_ERR: 10, + TYPE_MISMATCH_ERR: 11, + PATH_EXISTS_ERR: 12 + +}; + /** * Returns an an object that's converted by cordova to a FileEntry or a DirectoryEntry. * @param {boolean} isFile - is the object a file or a directory. true for file and false for directory. From dc9f85b9eb257eea3255b7c7ea80662e5147ea62 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Wed, 28 Dec 2022 21:58:18 +0530 Subject: [PATCH 22/32] Added documentation changes --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 3bf3ebd9..51bfcce8 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Although the object is in the global scope, it is not available to applications - OS X - Windows* - Browser +- Electron \* _These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`._ @@ -213,6 +214,19 @@ properties are `null`. \* The OS may periodically clear this directory +### Electron File System Layout +Varies according to OS and installation method. +| Device Path | `cordova.file.*` | r/w? | persistent? | OS clears | private | +|:------------------------------------------------------|:----------------------------|:----:|:-----------:|:---------:|:-------:| +| Windows:`~\AppData\Local\Programs\{appId}`
Linux: `@todo`
Mac: `/Applications/{appName.app}/Contents/Resources` | applicationDirectory | r | N/A | N/A | Yes | +| Windows:`~\AppData\Roaming\{appId}`
Linux: `@todo`
Mac: `~/Library/Application Support/{appId}` | dataDirectory | r/w | Yes | No | Yes | +| Windows: `~\AppData\Roaming`
Linux: `@todo`
Mac: `~/Library/Caches` | cacheDirectory | r/w | No | Yes\* | Yes | +| Windows: `~\AppData\Local\Temp`
Linux: `@todo`
Mac: `varies` | tempDirectory | r/w | No | Yes\* | Yes | +| Windows: `~\Documents`
Linux: `@todo`
Mac: `~/Documents` | documentsDirectory | - | - | - | - | + +\* The OS may periodically clear this directory + + ## Android Quirks ### Android Persistent storage location @@ -359,6 +373,10 @@ should be in the form `filesystem:file:///persistent/somefile.txt` as opposed to - INVALID_MODIFICATION_ERR (code: 9) is thrown instead of NO_MODIFICATION_ALLOWED_ERR(code: 6) on trying to call removeRecursively on the root file system. - INVALID_MODIFICATION_ERR (code: 9) is thrown instead of NOT_FOUND_ERR(code: 1) on trying to moveTo directory that does not exist. +### Electron quirks +- When using `cordova run electron`, the applicationDirectory is the electron platforms directory in the cordova project. i.e. `path/to/your/cordova/code/plaforms/electron/` +- When using a debug version, electron may switch to using the folder `Electron` instead of the folder `{appId}` as your dataDirectory. + ### IndexedDB-based impl quirks (Firefox and IE) - `.` and `..` are not supported. - IE does not support `file:///`-mode; only hosted mode is supported (http://localhost:xxxx). From da4f359f7428787edf5b6e965b7db390c18f644c Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Sun, 29 Jan 2023 21:40:02 +0530 Subject: [PATCH 23/32] added the two required functions --- src/electron/index.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/electron/index.js b/src/electron/index.js index 1b54eb50..78038406 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -289,7 +289,24 @@ module.exports = { * * @returns {Promise} resolves when file or directory is deleted. */ - removeRecursively: this.remove, + removeRecursively: function ([[fullPath]]) { + return new Promise((resolve, reject) => { + fs.stat(fullPath, (err, stats) => { + if (err) { + reject(FileError.NOT_FOUND_ERR); + return; + } + + fs.remove(fullPath, (err) => { + if (err) { + reject(FileError.NO_MODIFICATION_ALLOWED_ERR); + return; + } + resolve(); + }); + }); + }); + }, /** * Get the directory given the path and directory name. @@ -485,6 +502,18 @@ module.exports = { resolve(size); }); }); + }, + + requestFileSystem: function ([[type, size]]) { + if (type !== 0 && type !== 1) { + throw new Error(FileError.INVALID_MODIFICATION_ERR); + } + + const name = type === 0 ? 'temporary' : 'persistent'; + return { + name, + root: returnEntry(false, name, path.dirname(app.getAppPath()) + path.sep) + }; } }; From dc0f41178c31b98386a94e2e574ae4321346115d Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Mon, 27 Feb 2023 11:33:34 +0545 Subject: [PATCH 24/32] fixed the tests that deleted root --- src/electron/index.js | 2 +- tests/tests.js | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/electron/index.js b/src/electron/index.js index 78038406..211c6a6c 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -67,7 +67,7 @@ function returnEntry (isFile, name, fullPath, filesystem = null, nativeURL = nul name, fullPath, filesystem, - nativeURL + nativeURL: nativeURL ?? fullPath }; } diff --git a/tests/tests.js b/tests/tests.js index 671acc84..928d4c62 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -943,6 +943,10 @@ exports.defineAutoTests = function () { }); it('file.spec.36 removeRecursively on root file system', function (done) { + if (cordova.platformId === 'electron') { + pending('The "root.removeRecursively" method is not tested in Electron.'); + return; + } var remove = function (error) { expect(error).toBeDefined(); if (isChrome) { @@ -1391,6 +1395,10 @@ exports.defineAutoTests = function () { }); it('file.spec.56 remove on root file system', function (done) { + if (cordova.platformId === 'electron') { + pending('The "remove on root file system" method is not tested in Electron.'); + return; + } // remove entry that doesn't exist root.remove(succeed.bind(null, done, 'entry.remove - Unexpected success callback, it should not remove entry that it does not exists'), function (error) { expect(error).toBeDefined(); From 0e0740400282732f056b2cd89d6768ccf26c2ce6 Mon Sep 17 00:00:00 2001 From: --global Date: Mon, 17 Apr 2023 12:11:51 +0545 Subject: [PATCH 25/32] Fixed a few tests --- src/electron/index.js | 26 ++++++++++--------- tests/tests.js | 60 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 211c6a6c..bb94bb3f 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -84,7 +84,7 @@ module.exports = { return new Promise((resolve, reject) => { fs.readdir(fullPath, { withFileTypes: true }, (err, files) => { if (err) { - reject(err); + reject(FileError.NOT_FOUND_ERR); return; } @@ -270,13 +270,13 @@ module.exports = { return; } - fs.remove(fullPath, (err) => { - if (err) { - reject(FileError.NO_MODIFICATION_ALLOWED_ERR); - return; - } - resolve(); - }); + try { + fs.rmSync(fullPath); + } catch (error) { + reject(FileError.NO_MODIFICATION_ALLOWED_ERR); + return; + } + resolve(); }); }); }, @@ -375,7 +375,7 @@ module.exports = { .then(async () => { resolve(await getFile([[dstDir, dstName]])); }) - .catch(err => reject(err)); + .catch(() => reject(FileError.ENCODING_ERR)); }); }, @@ -512,7 +512,7 @@ module.exports = { const name = type === 0 ? 'temporary' : 'persistent'; return { name, - root: returnEntry(false, name, path.dirname(app.getAppPath()) + path.sep) + root: returnEntry(false, name, path.dirname(path.sep)) }; } }; @@ -576,8 +576,9 @@ function readAs (what, fullPath, encoding, startPos, endPos) { * * @returns {Promise} The file object that is converted to FileEntry by cordova. */ -function getFile ([[dstDir, dstName, options = {}]]) { +function getFile ([[dstDir, dstName, options]]) { const absolutePath = dstDir + dstName; + options = options || {}; return new Promise((resolve, reject) => { fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { @@ -647,8 +648,9 @@ function getFile ([[dstDir, dstName, options = {}]]) { * * @returns {Promise} The directory object that is converted to DirectoryEntry by cordova. */ -function getDirectory ([[dstDir, dstName, options = {}]]) { +function getDirectory ([[dstDir, dstName, options]]) { const absolutePath = dstDir + dstName; + options = options || {}; return new Promise((resolve, reject) => { fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { diff --git a/tests/tests.js b/tests/tests.js index 928d4c62..b0646a4a 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -30,6 +30,7 @@ exports.defineAutoTests = function () { var isIndexedDBShim = isBrowser && !isChrome; // Firefox and IE for example var isWindows = cordova.platformId === 'windows'; + var isElectron = cordova.platformId === 'electron'; /* eslint-enable no-undef */ var MEDIUM_TIMEOUT = 15000; @@ -266,7 +267,7 @@ exports.defineAutoTests = function () { }); it('file.spec.6 should error if you request a file system that is too large', function (done) { - if (isBrowser) { + if (isBrowser || isElectron) { /* window.requestFileSystem TEMPORARY and PERSISTENT filesystem quota is not limited in Chrome. Firefox filesystem size is not limited but every 50MB request user permission. IE10 allows up to 10mb of combined AppCache and IndexedDB used in implementation @@ -400,6 +401,9 @@ exports.defineAutoTests = function () { }); it('file.spec.10 resolve valid file name with parameters', function (done) { + if (isElectron) { + pending('fs does not take parameters in file'); + } var fileName = 'resolve.file.uri.params'; var win = function (fileEntry) { expect(fileEntry).toBeDefined(); @@ -422,6 +426,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -437,6 +443,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { // O.o chrome returns error code 0 + } else if (isElectron) { + // electron returns a not found error with error code 1 } else { expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef } @@ -499,6 +507,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -584,6 +594,8 @@ exports.defineAutoTests = function () { /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of PATH_EXISTS_ERR(code: 12) on trying to exclusively create a file, which already exists in Chrome. */ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + } else if (isElectron) { + expect(error.code.message).toContain(FileError.PATH_EXISTS_ERR); } else { expect(error).toBeFileError(FileError.PATH_EXISTS_ERR); // eslint-disable-line no-undef } @@ -638,6 +650,10 @@ exports.defineAutoTests = function () { (http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions). */ pending(); } + if (isElectron) { + /* the fs plugin will consider this a valid fileName and return a NOT FOUND ERROR */ + pending(); + } var fileName = 'de:invalid:path'; var fail = function (error) { @@ -657,6 +673,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -799,6 +817,8 @@ exports.defineAutoTests = function () { /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of PATH_EXISTS_ERR(code: 12) on trying to exclusively create a file or directory, which already exists (Chrome). */ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + } else if (isElectron) { + expect(error.code.message).toContain(FileError.PATH_EXISTS_ERR); } else { expect(error).toBeFileError(FileError.PATH_EXISTS_ERR); // eslint-disable-line no-undef } @@ -847,6 +867,11 @@ exports.defineAutoTests = function () { pending(); } + if (isElectron) { + /* the fs plugin will consider this a valid fileName and return a NOT FOUND ERROR */ + pending(); + } + var dirName = 'de:invalid:path'; var fail = function (error) { expect(error).toBeDefined(); @@ -866,6 +891,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { // chrome returns an unknown error with code 17 + } else if (isElectron) { + expect(error.code.message).toContain(FileError.TYPE_MISMATCH_ERR); } else { expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); // eslint-disable-line no-undef } @@ -890,6 +917,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { // chrome returns an unknown error with code 17 + } else if (isElectron) { + expect(error.code.message).toContain(FileError.TYPE_MISMATCH_ERR); } else { expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); // eslint-disable-line no-undef } @@ -914,6 +943,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1047,6 +1078,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1056,6 +1089,8 @@ exports.defineAutoTests = function () { expect(err).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1159,6 +1194,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1293,7 +1330,11 @@ exports.defineAutoTests = function () { }, function (entryFile) { var uri = entryFile.toURL(); expect(uri).toBeDefined(); - expect(uri).toContain('/num%201/num%202/'); + if (isElectron) { + expect(uri).toContain('/num 1/num 2/'); + } else { + expect(uri).toContain('/num%201/num%202/'); + } expect(uri.indexOf(rootPath)).not.toBe(-1); // cleanup deleteEntry(dirName_1, done); @@ -1311,6 +1352,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1332,7 +1375,12 @@ exports.defineAutoTests = function () { entry.remove(function () { root.getFile(fileName, null, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not get deleted file : ' + fileName), function (error) { expect(error).toBeDefined(); - expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef + + if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); + } else { + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef + } // cleanup deleteEntry(fileName, done); }); @@ -1350,6 +1398,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef + } else if (isElectron) { + expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1380,6 +1430,8 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { // chrome is returning unknown error with code 13 + } else if (isElectron) { + expect(error.code.message).toContain(FileError.ENCODING_ERR); } else { expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } @@ -1407,6 +1459,8 @@ exports.defineAutoTests = function () { NO_MODIFICATION_ALLOWED_ERR(code: 6) on trying to call removeRecursively on the root file system. */ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); + } else if (isElectron) { + expect(error.code.message).toContain(FileError.INVALID_MODIFICATION_ERR); } else { expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR); // eslint-disable-line no-undef } From e0b61848faf88a837b76541a3085964b98783d09 Mon Sep 17 00:00:00 2001 From: --global Date: Sun, 23 Apr 2023 01:39:51 +0545 Subject: [PATCH 26/32] fixed most tests and added required functionality --- src/electron/index.js | 117 +++++++++++++++++++++++------------------- tests/tests.js | 64 ++++++++--------------- 2 files changed, 86 insertions(+), 95 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index bb94bb3f..d853df91 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -84,7 +84,7 @@ module.exports = { return new Promise((resolve, reject) => { fs.readdir(fullPath, { withFileTypes: true }, (err, files) => { if (err) { - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); return; } @@ -135,7 +135,7 @@ module.exports = { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); return; } @@ -162,7 +162,7 @@ module.exports = { return new Promise((resolve, reject) => { fs.stat(url, (err, stats) => { if (err) { - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); return; } @@ -187,7 +187,7 @@ module.exports = { const modificationTime = metadataObject.modificationTime; const utimesError = function (err) { if (err) { - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); return; } resolve(); @@ -266,17 +266,18 @@ module.exports = { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); return; } - - try { - fs.rmSync(fullPath); - } catch (error) { - reject(FileError.NO_MODIFICATION_ALLOWED_ERR); + if (stats.isDirectory() && fs.readdirSync(fullPath).length !== 0) { + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); return; } - resolve(); + fs.remove(fullPath) + .then(() => resolve()) + .catch(() => { + reject(new Error(FileError.NO_MODIFICATION_ALLOWED_ERR)); + }); }); }); }, @@ -293,13 +294,13 @@ module.exports = { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); return; } fs.remove(fullPath, (err) => { if (err) { - reject(FileError.NO_MODIFICATION_ALLOWED_ERR); + reject(new Error(FileError.NO_MODIFICATION_ALLOWED_ERR)); return; } resolve(); @@ -348,14 +349,21 @@ module.exports = { */ copyTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { - fs.copyFile(srcPath, dstDir + dstName, async (err) => { - if (err) { - reject(FileError.INVALID_MODIFICATION_ERR); - return; - } - - resolve(await getFile([[dstDir, dstName]])); - }); + if (dstName.indexOf('/') !== -1 || path.resolve(srcPath) === path.resolve(dstDir + dstName)) { + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); + return; + } + if (!dstDir || !dstName) { + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); + return; + } + fs.stat(srcPath) + .then((stats) => { + fs.copy(srcPath, dstDir + dstName, { recursive: stats.isDirectory() }) + .then(async () => resolve(await stats.isDirectory() ? getDirectory([[dstDir, dstName]]) : getFile([[dstDir, dstName]]))) + .catch(() => reject(new Error(FileError.ENCODING_ERR))); + }) + .catch(() => reject(new Error(FileError.NOT_FOUND_ERR))); }); }, @@ -371,11 +379,21 @@ module.exports = { */ moveTo: function ([[srcPath, dstDir, dstName]]) { return new Promise((resolve, reject) => { - fs.move(srcPath, dstDir + dstName, { overwrite: true }) - .then(async () => { - resolve(await getFile([[dstDir, dstName]])); + if (dstName.indexOf('/') !== -1 || path.resolve(srcPath) === path.resolve(dstDir + dstName)) { + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); + return; + } + if (!dstDir || !dstName) { + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); + return; + } + fs.stat(srcPath) + .then((stats) => { + fs.move(srcPath, dstDir + dstName) + .then(async () => resolve(await stats.isDirectory() ? getDirectory([[dstDir, dstName]]) : getFile([[dstDir, dstName]]))) + .catch(() => reject(new Error(FileError.ENCODING_ERR))); }) - .catch(() => reject(FileError.ENCODING_ERR)); + .catch(() => reject(new Error(FileError.NOT_FOUND_ERR))); }); }, @@ -396,7 +414,7 @@ module.exports = { // support for cdvfile if (uri.trim().substr(0, 7) === 'cdvfile') { if (uri.indexOf('cdvfile://localhost') === -1) { - reject(FileError.ENCODING_ERR); + reject(new Error(FileError.ENCODING_ERR)); return; } @@ -411,7 +429,7 @@ module.exports = { } else if (indexTemporary !== -1) { // cdvfile://localhost/temporary/path/to/file uri = pathsPrefix.tempDirectory + uri.substr(indexTemporary + 10); } else { - reject(FileError.ENCODING_ERR); + reject(new Error(FileError.ENCODING_ERR)); return; } } @@ -463,7 +481,7 @@ module.exports = { write: function ([[fileName, data, position]]) { return new Promise((resolve, reject) => { if (!data) { - reject(FileError.INVALID_MODIFICATION_ERR); + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); return; } @@ -473,13 +491,11 @@ module.exports = { fs.open(fileName, 'a') .then(fd => { return fs.write(fd, buf, 0, buf.length, position) - .then(bw => { bytesWritten = bw; }) + .then(bw => { bytesWritten = bw.bytesWritten; }) .finally(() => fs.close(fd)); }) .then(() => resolve(bytesWritten)) - .catch(() => { - reject(FileError.INVALID_MODIFICATION_ERR); - }); + .catch(() => reject(new Error(FileError.INVALID_MODIFICATION_ERR))); }); }, @@ -495,7 +511,7 @@ module.exports = { return new Promise((resolve, reject) => { fs.truncate(fullPath, size, err => { if (err) { - reject(FileError.INVALID_STATE_ERR); + reject(new Error(FileError.INVALID_STATE_ERR)); return; } @@ -512,7 +528,7 @@ module.exports = { const name = type === 0 ? 'temporary' : 'persistent'; return { name, - root: returnEntry(false, name, path.dirname(path.sep)) + root: returnEntry(false, name, '/') }; } }; @@ -535,7 +551,7 @@ function readAs (what, fullPath, encoding, startPos, endPos) { return new Promise((resolve, reject) => { fs.open(fullPath, 'r', (err, fd) => { if (err) { - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); return; } @@ -558,9 +574,7 @@ function readAs (what, fullPath, encoding, startPos, endPos) { break; } }) - .catch(() => { - reject(FileError.NOT_READABLE_ERR); - }) + .catch(() => reject(new Error(FileError.NOT_READABLE_ERR))) .then(() => fs.close(fd)); }); }); @@ -582,7 +596,7 @@ function getFile ([[dstDir, dstName, options]]) { return new Promise((resolve, reject) => { fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { - reject(FileError.INVALID_STATE_ERR); + reject(new Error(FileError.INVALID_STATE_ERR)); return; } @@ -592,13 +606,13 @@ function getFile ([[dstDir, dstName, options]]) { function createFile () { fs.open(absolutePath, 'w', (err, fd) => { if (err) { - reject(FileError.INVALID_STATE_ERR); + reject(new Error(FileError.INVALID_STATE_ERR)); return; } fs.close(fd, (err) => { if (err) { - reject(FileError.INVALID_STATE_ERR); + reject(new Error(FileError.INVALID_STATE_ERR)); return; } resolve(returnEntry(true, baseName, absolutePath)); @@ -609,7 +623,7 @@ function getFile ([[dstDir, dstName, options]]) { if (options.create === true && options.exclusive === true && exists) { // If create and exclusive are both true, and the path already exists, // getFile must fail. - reject(FileError.PATH_EXISTS_ERR); + reject(new Error(FileError.PATH_EXISTS_ERR)); } else if (options.create === true && !exists) { // If create is true, the path doesn't exist, and no other error occurs, // getFile must create it as a zero-length file and return a corresponding @@ -620,15 +634,15 @@ function getFile ([[dstDir, dstName, options]]) { // Overwrite file, delete then create new. createFile(); } else { - reject(FileError.INVALID_MODIFICATION_ERR); + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); } } else if (!options.create && !exists) { // If create is not true and the path doesn't exist, getFile must fail. - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); } else if (!options.create && exists && stats.isDirectory()) { // If create is not true and the path exists, but is a directory, getFile // must fail. - reject(FileError.TYPE_MISMATCH_ERR); + reject(new Error(FileError.TYPE_MISMATCH_ERR)); } else { // Otherwise, if no other error occurs, getFile must return a FileEntry // corresponding to path. @@ -654,24 +668,23 @@ function getDirectory ([[dstDir, dstName, options]]) { return new Promise((resolve, reject) => { fs.stat(absolutePath, (err, stats) => { if (err && err.message && err.message.indexOf('ENOENT') !== 0) { - reject(FileError.INVALID_STATE_ERR); + reject(new Error(FileError.INVALID_STATE_ERR)); return; } const exists = !err; const baseName = path.basename(absolutePath); - if (options.create === true && options.exclusive === true && exists) { // If create and exclusive are both true, and the path already exists, // getDirectory must fail. - reject(FileError.PATH_EXISTS_ERR); + reject(new Error(FileError.PATH_EXISTS_ERR)); } else if (options.create === true && !exists) { // If create is true, the path doesn't exist, and no other error occurs, // getDirectory must create it as a zero-length file and return a corresponding // MyDirectoryEntry. fs.mkdir(absolutePath, (err) => { if (err) { - reject(FileError.PATH_EXISTS_ERR); + reject(new Error(FileError.PATH_EXISTS_ERR)); return; } resolve(returnEntry(false, baseName, absolutePath)); @@ -680,15 +693,15 @@ function getDirectory ([[dstDir, dstName, options]]) { if (stats.isDirectory()) { resolve(returnEntry(false, baseName, absolutePath)); } else { - reject(FileError.INVALID_MODIFICATION_ERR); + reject(new Error(FileError.INVALID_MODIFICATION_ERR)); } } else if (!options.create && !exists) { // If create is not true and the path doesn't exist, getDirectory must fail. - reject(FileError.NOT_FOUND_ERR); + reject(new Error(FileError.NOT_FOUND_ERR)); } else if (!options.create && exists && stats.isFile()) { // If create is not true and the path exists, but is a file, getDirectory // must fail. - reject(FileError.TYPE_MISMATCH_ERR); + reject(new Error(FileError.TYPE_MISMATCH_ERR)); } else { // Otherwise, if no other error occurs, getDirectory must return a // DirectoryEntry corresponding to path. diff --git a/tests/tests.js b/tests/tests.js index b0646a4a..e18ef843 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -60,6 +60,9 @@ exports.defineAutoTests = function () { return { compare: function (error, code) { var pass = error.code === code; + if(isElectron && error && error.code && error.code.message) { + pass = error.code.message.indexOf(code) > -1; + } return { pass: pass, message: 'Expected FileError with code ' + fileErrorMap[error.code] + ' (' + error.code + ') to be ' + fileErrorMap[code] + '(' + code + ')' @@ -426,8 +429,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -507,8 +508,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -594,8 +593,6 @@ exports.defineAutoTests = function () { /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of PATH_EXISTS_ERR(code: 12) on trying to exclusively create a file, which already exists in Chrome. */ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); - } else if (isElectron) { - expect(error.code.message).toContain(FileError.PATH_EXISTS_ERR); } else { expect(error).toBeFileError(FileError.PATH_EXISTS_ERR); // eslint-disable-line no-undef } @@ -673,8 +670,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -817,8 +812,6 @@ exports.defineAutoTests = function () { /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of PATH_EXISTS_ERR(code: 12) on trying to exclusively create a file or directory, which already exists (Chrome). */ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); - } else if (isElectron) { - expect(error.code.message).toContain(FileError.PATH_EXISTS_ERR); } else { expect(error).toBeFileError(FileError.PATH_EXISTS_ERR); // eslint-disable-line no-undef } @@ -891,8 +884,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { // chrome returns an unknown error with code 17 - } else if (isElectron) { - expect(error.code.message).toContain(FileError.TYPE_MISMATCH_ERR); } else { expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); // eslint-disable-line no-undef } @@ -917,8 +908,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { // chrome returns an unknown error with code 17 - } else if (isElectron) { - expect(error.code.message).toContain(FileError.TYPE_MISMATCH_ERR); } else { expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR); // eslint-disable-line no-undef } @@ -943,8 +932,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1078,8 +1065,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1089,8 +1074,6 @@ exports.defineAutoTests = function () { expect(err).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1194,8 +1177,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1352,8 +1333,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1375,12 +1354,7 @@ exports.defineAutoTests = function () { entry.remove(function () { root.getFile(fileName, null, succeed.bind(null, done, 'root.getFile - Unexpected success callback, it should not get deleted file : ' + fileName), function (error) { expect(error).toBeDefined(); - - if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); - } else { - expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef - } + expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef // cleanup deleteEntry(fileName, done); }); @@ -1398,8 +1372,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { expect(error).toBeFileError(FileError.SYNTAX_ERR); // eslint-disable-line no-undef - } else if (isElectron) { - expect(error.code.message).toContain(FileError.NOT_FOUND_ERR); } else { expect(error).toBeFileError(FileError.NOT_FOUND_ERR); // eslint-disable-line no-undef } @@ -1430,8 +1402,6 @@ exports.defineAutoTests = function () { expect(error).toBeDefined(); if (isChrome) { // chrome is returning unknown error with code 13 - } else if (isElectron) { - expect(error.code.message).toContain(FileError.ENCODING_ERR); } else { expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } @@ -1460,7 +1430,7 @@ exports.defineAutoTests = function () { on the root file system. */ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); } else if (isElectron) { - expect(error.code.message).toContain(FileError.INVALID_MODIFICATION_ERR); + expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); } else { expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR); // eslint-disable-line no-undef } @@ -1681,7 +1651,7 @@ exports.defineAutoTests = function () { if (isChrome) { // chrome returns unknown error with code 13 } else { - expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(isElectron ? FileError.ENCODING_ERR : FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } root.getDirectory(srcDir, { create: false @@ -1697,6 +1667,10 @@ exports.defineAutoTests = function () { }); it('file.spec.63 copyTo: directory that does not exist', function (done) { + if (isElectron) { + // Electron creates the folder if it doesn't exist + pending() + } var file1 = 'entry.copy.dnf.file1'; var dirName = 'dir-foo'; createFile(file1, function (fileEntry) { @@ -1730,7 +1704,7 @@ exports.defineAutoTests = function () { // copy file1 to file2 entry.copyTo(root, file2, succeed.bind(null, done, 'entry.copyTo - Unexpected success callback, it should not copy a file ' + file1 + ' to an invalid file name: ' + file2), function (error) { expect(error).toBeDefined(); - expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef // cleanup deleteEntry(file1, done); }); @@ -2078,7 +2052,7 @@ exports.defineAutoTests = function () { if (isChrome) { // chrome returns unknown error with code 13 } else { - expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(isElectron ? FileError.ENCODING_ERR : FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } // make sure original directory still exists root.getDirectory(srcDir, { @@ -2165,7 +2139,7 @@ exports.defineAutoTests = function () { if (isChrome) { // chrome returns unknown error with code 13 } else { - expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(isElectron ? FileError.ENCODING_ERR : FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } // check that original dir still exists directory.getDirectory(subDir, { @@ -2214,7 +2188,7 @@ exports.defineAutoTests = function () { if (isChrome) { // chrome returns unknown error with code 13 } else { - expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(isElectron ? FileError.ENCODING_ERR : FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } // test that original directory exists root.getDirectory(srcDir, { @@ -2262,7 +2236,7 @@ exports.defineAutoTests = function () { if (isChrome) { // chrome returns unknown error with code 13 } else { - expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(isElectron ? FileError.ENCODING_ERR : FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } // check that original dir still exists root.getDirectory(srcDir, { @@ -2317,7 +2291,7 @@ exports.defineAutoTests = function () { if (isChrome) { // chrome returns unknown error with code 13 } else { - expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(isElectron ? FileError.ENCODING_ERR : FileError.INVALID_MODIFICATION_ERR); // eslint-disable-line no-undef } // making sure destination directory still exists directory.getDirectory(subDir, { @@ -2452,6 +2426,9 @@ exports.defineAutoTests = function () { if (isChrome) { pending('chrome freak out about non-existend dir not being a DirectoryEntry'); } + if(isElectron) { + pending('Electron creates the directory if it doesn\'t exist'); + } var file1 = 'entry.move.dnf.file1'; var dstDir = 'entry.move.dnf.dstDir'; var dstPath = joinURL(root.fullPath, dstDir); @@ -3502,6 +3479,8 @@ exports.defineAutoTests = function () { if (cordova.platformId === 'android') { // Starting from Cordova-Android 10.x, the app content is served from the https scheme pathExpect = 'https://'; + } else if(isElectron) { + pathExpect = '/native'; } else if (isChrome) { pathExpect = 'filesystem:http://'; } @@ -4048,7 +4027,6 @@ exports.defineAutoTests = function () { }, failed.bind(this, done, 'root.getDirectory - Error creating directory : ' + dirName)); }); }); - // Content and Asset URLs if (cordova.platformId === 'android') { // eslint-disable-line no-undef describe('content: URLs', function () { From 9012ee93d95a44d4f0f25f0d7848675e270016a0 Mon Sep 17 00:00:00 2001 From: --global Date: Tue, 25 Apr 2023 13:09:41 +0545 Subject: [PATCH 27/32] eslint fixes --- tests/tests.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/tests.js b/tests/tests.js index e120887a..1145ca9c 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -60,7 +60,7 @@ exports.defineAutoTests = function () { return { compare: function (error, code) { var pass = error.code === code; - if(isElectron && error && error.code && error.code.message) { + if (isElectron && error && error.code && error.code.message) { pass = error.code.message.indexOf(code) > -1; } return { @@ -1669,7 +1669,7 @@ exports.defineAutoTests = function () { it('file.spec.63 copyTo: directory that does not exist', function (done) { if (isElectron) { // Electron creates the folder if it doesn't exist - pending() + pending(); } var file1 = 'entry.copy.dnf.file1'; var dirName = 'dir-foo'; @@ -1704,7 +1704,7 @@ exports.defineAutoTests = function () { // copy file1 to file2 entry.copyTo(root, file2, succeed.bind(null, done, 'entry.copyTo - Unexpected success callback, it should not copy a file ' + file1 + ' to an invalid file name: ' + file2), function (error) { expect(error).toBeDefined(); - expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef + expect(error).toBeFileError(FileError.ENCODING_ERR); // eslint-disable-line no-undef // cleanup deleteEntry(file1, done); }); @@ -2426,7 +2426,7 @@ exports.defineAutoTests = function () { if (isChrome) { pending('chrome freak out about non-existend dir not being a DirectoryEntry'); } - if(isElectron) { + if (isElectron) { pending('Electron creates the directory if it doesn\'t exist'); } var file1 = 'entry.move.dnf.file1'; From fb0a2c88f28f1a3248883181052a92d030093dbb Mon Sep 17 00:00:00 2001 From: --global Date: Thu, 27 Apr 2023 22:37:53 +0545 Subject: [PATCH 28/32] updated tests and code --- src/electron/index.js | 6 +++--- tests/tests.js | 30 ++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index d853df91..24acb113 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -591,7 +591,7 @@ function readAs (what, fullPath, encoding, startPos, endPos) { * @returns {Promise} The file object that is converted to FileEntry by cordova. */ function getFile ([[dstDir, dstName, options]]) { - const absolutePath = dstDir + dstName; + const absolutePath = path.join(dstDir, dstName); options = options || {}; return new Promise((resolve, reject) => { fs.stat(absolutePath, (err, stats) => { @@ -615,7 +615,7 @@ function getFile ([[dstDir, dstName, options]]) { reject(new Error(FileError.INVALID_STATE_ERR)); return; } - resolve(returnEntry(true, baseName, absolutePath)); + resolve(returnEntry(true, baseName, absolutePath.replace('\\', '/'))); }); }); } @@ -646,7 +646,7 @@ function getFile ([[dstDir, dstName, options]]) { } else { // Otherwise, if no other error occurs, getFile must return a FileEntry // corresponding to path. - resolve(returnEntry(true, baseName, absolutePath)); + resolve(returnEntry(true, baseName, absolutePath.replace('\\', '/'))); } }); }); diff --git a/tests/tests.js b/tests/tests.js index 1145ca9c..978d7ae1 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -962,7 +962,7 @@ exports.defineAutoTests = function () { it('file.spec.36 removeRecursively on root file system', function (done) { if (cordova.platformId === 'electron') { - pending('The "root.removeRecursively" method is not tested in Electron.'); + pending('The "root.removeRecursively" method will not be tested in Electron as it would remove the entire source code and cause further tests to fail.'); return; } var remove = function (error) { @@ -1417,20 +1417,14 @@ exports.defineAutoTests = function () { }); it('file.spec.56 remove on root file system', function (done) { - if (cordova.platformId === 'electron') { - pending('The "remove on root file system" method is not tested in Electron.'); - return; - } // remove entry that doesn't exist root.remove(succeed.bind(null, done, 'entry.remove - Unexpected success callback, it should not remove entry that it does not exists'), function (error) { expect(error).toBeDefined(); - if (isChrome) { + if (isChrome || isElectron) { /* INVALID_MODIFICATION_ERR (code: 9) or ??? (code: 13) is thrown instead of NO_MODIFICATION_ALLOWED_ERR(code: 6) on trying to call removeRecursively on the root file system. */ // expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); - } else if (isElectron) { - expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR); } else { expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR); // eslint-disable-line no-undef } @@ -2320,6 +2314,9 @@ exports.defineAutoTests = function () { }); it('file.spec.77 moveTo: file replace existing file', function (done) { + if (isElectron) { + pending('Electron throws an error because of file overwrites') + } var file1 = 'entry.move.frf.file1'; var file2 = 'entry.move.frf.file2'; var file2Path = joinURL(root.fullPath, file2); @@ -2368,6 +2365,9 @@ exports.defineAutoTests = function () { /* `copyTo` and `moveTo` functions do not support directories (Firefox, IE) */ pending(); } + if (isElectron) { + pending('Electron throws an error because of overwrites') + } var file1 = 'file1'; var srcDir = 'entry.move.drd.srcDir'; @@ -2932,6 +2932,10 @@ exports.defineAutoTests = function () { }); it('file.spec.98 should be able to seek to the middle of the file and write more data than file.length', function (done) { + if(isElectron) { + pending('Electron implements fs-extra for node. This means writing from a particular seek point doesn\'t remove data from the back'); + return; + } var fileName = 'writer.seek.write'; // file content var content = 'This is our sentence.'; // for checking file length var exception = 'newer sentence.'; @@ -2976,6 +2980,10 @@ exports.defineAutoTests = function () { i.e. the length is not being changed from content.length and writer length will be equal 21 */ pending(); } + if(isElectron) { + pending('Electron implements fs-extra for node. This means writing from a particular seek point doesn\'t remove data from the back'); + return; + } var fileName = 'writer.seek.write2'; // file content var content = 'This is our sentence.'; // for checking file length @@ -3621,6 +3629,9 @@ exports.defineAutoTests = function () { }); it('file.spec.123 should resolve native URLs returned by API with query string', function (done) { + if(isElectron) { + pending('not supported in Electron') + } var fileName = 'native.resolve.uri3'; // create a new file entry createFile(fileName, function (entry) { @@ -3635,6 +3646,9 @@ exports.defineAutoTests = function () { }); it('file.spec.124 should resolve native URLs returned by API with localhost and query string', function (done) { + if(isElectron) { + pending('not supported in Electron') + } var fileName = 'native.resolve.uri4'; // create a new file entry createFile(fileName, function (entry) { From d8097d53eed0abf206970c1c199893af377b0ee2 Mon Sep 17 00:00:00 2001 From: --global Date: Thu, 27 Apr 2023 23:11:51 +0545 Subject: [PATCH 29/32] fixed linting --- tests/tests.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/tests.js b/tests/tests.js index 978d7ae1..b633f954 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -2315,7 +2315,7 @@ exports.defineAutoTests = function () { it('file.spec.77 moveTo: file replace existing file', function (done) { if (isElectron) { - pending('Electron throws an error because of file overwrites') + pending('Electron throws an error because of file overwrites'); } var file1 = 'entry.move.frf.file1'; var file2 = 'entry.move.frf.file2'; @@ -2366,7 +2366,7 @@ exports.defineAutoTests = function () { pending(); } if (isElectron) { - pending('Electron throws an error because of overwrites') + pending('Electron throws an error because of overwrites'); } var file1 = 'file1'; @@ -2932,7 +2932,7 @@ exports.defineAutoTests = function () { }); it('file.spec.98 should be able to seek to the middle of the file and write more data than file.length', function (done) { - if(isElectron) { + if (isElectron) { pending('Electron implements fs-extra for node. This means writing from a particular seek point doesn\'t remove data from the back'); return; } @@ -2980,7 +2980,7 @@ exports.defineAutoTests = function () { i.e. the length is not being changed from content.length and writer length will be equal 21 */ pending(); } - if(isElectron) { + if (isElectron) { pending('Electron implements fs-extra for node. This means writing from a particular seek point doesn\'t remove data from the back'); return; } @@ -3629,8 +3629,8 @@ exports.defineAutoTests = function () { }); it('file.spec.123 should resolve native URLs returned by API with query string', function (done) { - if(isElectron) { - pending('not supported in Electron') + if (isElectron) { + pending('not supported in Electron'); } var fileName = 'native.resolve.uri3'; // create a new file entry @@ -3646,8 +3646,8 @@ exports.defineAutoTests = function () { }); it('file.spec.124 should resolve native URLs returned by API with localhost and query string', function (done) { - if(isElectron) { - pending('not supported in Electron') + if (isElectron) { + pending('not supported in Electron'); } var fileName = 'native.resolve.uri4'; // create a new file entry From d9db774ad079cf33ae7673e114399c250f91b734 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Thu, 14 Dec 2023 00:22:33 +0545 Subject: [PATCH 30/32] fixed lint errors --- src/electron/index.js | 2 +- tests/tests.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 24acb113..02aca886 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -319,7 +319,7 @@ module.exports = { * * @returns {Promise} The directory object that is converted to DirectoryEntry by cordova. */ - getDirectory: getDirectory, + getDirectory, /** * Get the Parent directory diff --git a/tests/tests.js b/tests/tests.js index 93024d92..153d4dca 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -59,7 +59,7 @@ exports.defineAutoTests = function () { toBeFileError: function () { return { compare: function (error, code) { - const pass = error.code === code; + let pass = error.code === code; if (isElectron && error && error.code && error.code.message) { pass = error.code.message.indexOf(code) > -1; } From e661af84d79477473b28afd5ba138c85c5f8369f Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Fri, 19 Jan 2024 22:13:46 +0545 Subject: [PATCH 31/32] Updated Readme to include linux locations --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5a4e89fa..89ebb072 100644 --- a/README.md +++ b/README.md @@ -235,14 +235,16 @@ If interfacing with the external file system is a requirement for your applicati ### Electron File System Layout -Varies according to OS and installation method. +Varies according to the Operating System. The installation method also has an impact on the file path and where it is installed. The file path will be different if you're running it using the command `cordova run electron --nobuild` (debug) or if you've published to a store and running the program through the app installed from the store. The list can be expanded. + +_Note: The current Linux file paths are tested in Ubuntu and might be different depending on the distribution of linux_ | Device Path | `cordova.file.*` | r/w? | persistent? | OS clears | private | |:------------------------------------------------------|:----------------------------|:----:|:-----------:|:---------:|:-------:| -| Windows:`~\AppData\Local\Programs\{appId}`
Linux: `@todo`
Mac: `/Applications/{appName.app}/Contents/Resources` | applicationDirectory | r | N/A | N/A | Yes | -| Windows:`~\AppData\Roaming\{appId}`
Linux: `@todo`
Mac: `~/Library/Application Support/{appId}` | dataDirectory | r/w | Yes | No | Yes | -| Windows: `~\AppData\Roaming`
Linux: `@todo`
Mac: `~/Library/Caches` | cacheDirectory | r/w | No | Yes\* | Yes | -| Windows: `~\AppData\Local\Temp`
Linux: `@todo`
Mac: `varies` | tempDirectory | r/w | No | Yes\* | Yes | -| Windows: `~\Documents`
Linux: `@todo`
Mac: `~/Documents` | documentsDirectory | - | - | - | - | +| __Windows:__
_Debug:_ `{cwd}\\platforms\\electron`
_Installer:_`~\\AppData\\Local\\Programs\\{appId}`
__Linux:__
_Debug:_ `{cwd}/platforms/electron`
_Package Installer:_ `/opt/{appName}/resources`
__Mac:__
_Debug:_ `{cwd}/platforms/electron`
_Installer:_`/Applications/{appName.app}/Contents/Resources` | applicationDirectory | r | N/A | N/A | Yes | +| __Windows__:
_Debug:_ `~\\AppData\\Roaming\\Electron\\`
_Installer:_`~\\AppData\\Local\\Programs\\{appId}`
__Linux:__
_Debug:_ `~/.config/Electron/`
_Package Installer:_ `~/.config/{appId}/`
__Mac:__ `~/Library/Application Support/{appId}` | dataDirectory | r/w | Yes | No | Yes | +| __Windows__:
_Debug:_ `~\\AppData\\Roaming\\`
_Installer:_`~\\AppData\\Roaming\\`
__Linux:__
_Debug:_ `~/.cache/`
_Installer:_`~/.cache/`
__Mac:__ `~/Library/Caches` | cacheDirectory | r/w | No | Yes\* | Yes | +| __Windows__:
_Debug:_ `~\\AppData\\Local\\Temp\\`
_Installer:_`~\AppData\\Local\\Temp\\`
__Linux:__
_Debug:_ `/tmp/`
_Package Installer:_ `/tmp/`
__Mac:__ `varies` | tempDirectory | r/w | No | Yes\* | Yes | +| Windows: `~\\Documents`
Linux: `~/Documents/`
Mac: `~/Documents` | documentsDirectory | - | - | - | - | \* The OS may periodically clear this directory From 0ee7b96550221fb33b35414bc40860b342720621 Mon Sep 17 00:00:00 2001 From: Prasun Jajodia Date: Wed, 21 Feb 2024 13:10:35 +0545 Subject: [PATCH 32/32] Updated files to accept new single array args --- src/electron/index.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/electron/index.js b/src/electron/index.js index 02aca886..a654501c 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -80,7 +80,7 @@ module.exports = { * @returns {Promise} - An array of Entries in that directory * */ - readEntries: function ([[fullPath]]) { + readEntries: function ([fullPath]) { return new Promise((resolve, reject) => { fs.readdir(fullPath, { withFileTypes: true }, (err, files) => { if (err) { @@ -131,7 +131,7 @@ module.exports = { * fullPath: the full path of the file including the extension. * @returns {Promise} - An Object containing the file metadata. */ - getFileMetadata: function ([[fullPath]]) { + getFileMetadata: function ([fullPath]) { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { @@ -158,7 +158,7 @@ module.exports = { * fullPath: the full path of the file or directory. * @returns {Promise} - An Object containing the metadata. */ - getMetadata: function ([[url]]) { + getMetadata: function ([url]) { return new Promise((resolve, reject) => { fs.stat(url, (err, stats) => { if (err) { @@ -182,7 +182,7 @@ module.exports = { * metadataObject: the object containing metadataValues (currently only supports modificationTime) * @returns {Promise} - An Object containing the file metadata. */ - setMetadata: function ([[fullPath, metadataObject]]) { + setMetadata: function ([fullPath, metadataObject]) { return new Promise((resolve, reject) => { const modificationTime = metadataObject.modificationTime; const utimesError = function (err) { @@ -208,7 +208,7 @@ module.exports = { * * @returns {Promise} The string value within the file. */ - readAsText: function ([[fileName, enc, startPos, endPos]]) { + readAsText: function ([fileName, enc, startPos, endPos]) { return readAs('text', fileName, enc, startPos, endPos); }, @@ -222,7 +222,7 @@ module.exports = { * * @returns {Promise} the file as a dataUrl. */ - readAsDataURL: function ([[fileName, startPos, endPos]]) { + readAsDataURL: function ([fileName, startPos, endPos]) { return readAs('dataURL', fileName, null, startPos, endPos); }, @@ -236,7 +236,7 @@ module.exports = { * * @returns {Promise} The file as a binary string. */ - readAsBinaryString: function ([[fileName, startPos, endPos]]) { + readAsBinaryString: function ([fileName, startPos, endPos]) { return readAs('binaryString', fileName, null, startPos, endPos); }, @@ -250,7 +250,7 @@ module.exports = { * * @returns {Promise} The file as an arrayBuffer. */ - readAsArrayBuffer: function ([[fileName, startPos, endPos]]) { + readAsArrayBuffer: function ([fileName, startPos, endPos]) { return readAs('arrayBuffer', fileName, null, startPos, endPos); }, @@ -262,7 +262,7 @@ module.exports = { * * @returns {Promise} resolves when file or directory is deleted. */ - remove: function ([[fullPath]]) { + remove: function ([fullPath]) { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { @@ -290,7 +290,7 @@ module.exports = { * * @returns {Promise} resolves when file or directory is deleted. */ - removeRecursively: function ([[fullPath]]) { + removeRecursively: function ([fullPath]) { return new Promise((resolve, reject) => { fs.stat(fullPath, (err, stats) => { if (err) { @@ -329,7 +329,7 @@ module.exports = { * * @returns {Promise} The parent directory object that is converted to DirectoryEntry by cordova. */ - getParent: function ([[url]]) { + getParent: function ([url]) { const parentPath = path.dirname(url); const parentName = path.basename(parentPath); const fullPath = path.dirname(parentPath) + path.sep; @@ -347,7 +347,7 @@ module.exports = { * * @returns {Promise} The copied file. */ - copyTo: function ([[srcPath, dstDir, dstName]]) { + copyTo: function ([srcPath, dstDir, dstName]) { return new Promise((resolve, reject) => { if (dstName.indexOf('/') !== -1 || path.resolve(srcPath) === path.resolve(dstDir + dstName)) { reject(new Error(FileError.INVALID_MODIFICATION_ERR)); @@ -360,7 +360,7 @@ module.exports = { fs.stat(srcPath) .then((stats) => { fs.copy(srcPath, dstDir + dstName, { recursive: stats.isDirectory() }) - .then(async () => resolve(await stats.isDirectory() ? getDirectory([[dstDir, dstName]]) : getFile([[dstDir, dstName]]))) + .then(async () => resolve(await stats.isDirectory() ? getDirectory([dstDir, dstName]) : getFile([dstDir, dstName]))) .catch(() => reject(new Error(FileError.ENCODING_ERR))); }) .catch(() => reject(new Error(FileError.NOT_FOUND_ERR))); @@ -377,7 +377,7 @@ module.exports = { * * @returns {Promise} The moved file. */ - moveTo: function ([[srcPath, dstDir, dstName]]) { + moveTo: function ([srcPath, dstDir, dstName]) { return new Promise((resolve, reject) => { if (dstName.indexOf('/') !== -1 || path.resolve(srcPath) === path.resolve(dstDir + dstName)) { reject(new Error(FileError.INVALID_MODIFICATION_ERR)); @@ -390,7 +390,7 @@ module.exports = { fs.stat(srcPath) .then((stats) => { fs.move(srcPath, dstDir + dstName) - .then(async () => resolve(await stats.isDirectory() ? getDirectory([[dstDir, dstName]]) : getFile([[dstDir, dstName]]))) + .then(async () => resolve(await stats.isDirectory() ? getDirectory([dstDir, dstName]) : getFile([dstDir, dstName]))) .catch(() => reject(new Error(FileError.ENCODING_ERR))); }) .catch(() => reject(new Error(FileError.NOT_FOUND_ERR))); @@ -404,7 +404,7 @@ module.exports = { * uri: The full path for the file. * @returns {Promise} The entry for the file or directory. */ - resolveLocalFileSystemURI: function ([[uri]]) { + resolveLocalFileSystemURI: function ([uri]) { return new Promise((resolve, reject) => { // support for encodeURI if (/\%5/g.test(uri) || /\%20/g.test(uri)) { // eslint-disable-line no-useless-escape @@ -478,7 +478,7 @@ module.exports = { * position: the position offset to start writing from. * @returns {Promise} An object with information about the amount of bytes written. */ - write: function ([[fileName, data, position]]) { + write: function ([fileName, data, position]) { return new Promise((resolve, reject) => { if (!data) { reject(new Error(FileError.INVALID_MODIFICATION_ERR)); @@ -507,7 +507,7 @@ module.exports = { * size: the length of the file to truncate to. * @returns {Promise} */ - truncate: function ([[fullPath, size]]) { + truncate: function ([fullPath, size]) { return new Promise((resolve, reject) => { fs.truncate(fullPath, size, err => { if (err) { @@ -520,7 +520,7 @@ module.exports = { }); }, - requestFileSystem: function ([[type, size]]) { + requestFileSystem: function ([type, size]) { if (type !== 0 && type !== 1) { throw new Error(FileError.INVALID_MODIFICATION_ERR); } @@ -590,7 +590,7 @@ function readAs (what, fullPath, encoding, startPos, endPos) { * * @returns {Promise} The file object that is converted to FileEntry by cordova. */ -function getFile ([[dstDir, dstName, options]]) { +function getFile ([dstDir, dstName, options]) { const absolutePath = path.join(dstDir, dstName); options = options || {}; return new Promise((resolve, reject) => { @@ -662,7 +662,7 @@ function getFile ([[dstDir, dstName, options]]) { * * @returns {Promise} The directory object that is converted to DirectoryEntry by cordova. */ -function getDirectory ([[dstDir, dstName, options]]) { +function getDirectory ([dstDir, dstName, options]) { const absolutePath = dstDir + dstName; options = options || {}; return new Promise((resolve, reject) => {