diff --git a/.putout.json b/.putout.json index d43c63d4..b868f59d 100644 --- a/.putout.json +++ b/.putout.json @@ -8,7 +8,6 @@ "*.md" ], "rules": { - "tape/remove-skip": "off", "package-json/add-type": "off" }, "match": { diff --git a/.webpack/js.mjs b/.webpack/js.mjs index d616745a..d52e0388 100644 --- a/.webpack/js.mjs +++ b/.webpack/js.mjs @@ -127,7 +127,7 @@ export default { [`${modules}/config`]: `${dirModules}/config/index.js`, [`${modules}/contact`]: `${dirModules}/contact.js`, [`${modules}/upload`]: `${dirModules}/upload.js`, - [`${modules}/operation`]: `${dirModules}/operation/index.js`, + [`${modules}/operation`]: `${dirModules}/operation/index.mjs`, [`${modules}/konsole`]: `${dirModules}/konsole.js`, [`${modules}/terminal`]: `${dirModules}/terminal.js`, [`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`, diff --git a/ChangeLog b/ChangeLog index 235cd94d..edf5330e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2026.01.21, v19.1.9 + +feature: +- 75ad4415 cloudcmd: @putout/eslint-flat v4.0.0 +- c5d9bd7c client: key: vim: get rid of mock-require +- f437a52f client: images: migrate to EMS +- 7192a56e client: dom: current-file: migrate to ESM + +2026.01.20, v19.1.8 + +fix: +- 8a769fd5 client: modules: operation: no update after copy + +feature: +- d574a93d client: key: migrate to ESM +- 3b409074 client: modules: operation: migrate to ESM +- 3b6b0b5a client: buffer: migrate to ESM +- 8876f050 cloudcmd: eslint-plugin-putout v30.0.0 + 2026.01.17, v19.1.7 feature: diff --git a/HELP.md b/HELP.md index 7f9d7a58..68087875 100644 --- a/HELP.md +++ b/HELP.md @@ -1,4 +1,4 @@ -# Cloud Commander v19.1.7 +# Cloud Commander v19.1.9 ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] @@ -1111,6 +1111,8 @@ There are a lot of ways to be involved in `Cloud Commander` development: ## Version history +- *2026.01.21*, **[v19.1.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.9)** +- *2026.01.20*, **[v19.1.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.8)** - *2026.01.17*, **[v19.1.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.7)** - *2026.01.16*, **[v19.1.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.6)** - *2026.01.16*, **[v19.1.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.5)** diff --git a/README.md b/README.md index 62a40452..8e510161 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cloud Commander v19.1.7 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] +# Cloud Commander v19.1.9 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] diff --git a/client/client.mjs b/client/client.mjs index b1d9edd6..b6c3a436 100644 --- a/client/client.mjs +++ b/client/client.mjs @@ -9,10 +9,10 @@ import {tryToCatch} from 'try-to-catch'; import {addSlashToEnd} from 'format-io'; import pascalCase from 'just-pascal-case'; import currify from 'currify'; -import Images from './dom/images.js'; +import * as Images from './dom/images.mjs'; import {unregisterSW} from './sw/register.js'; import {getJsonFromFileTable} from './get-json-from-file-table.mjs'; -import Key from './key/index.js'; +import {Key} from './key/index.mjs'; import { apiURL, formatMsg, diff --git a/client/dom/buffer.js b/client/dom/buffer.js deleted file mode 100644 index afa78dd7..00000000 --- a/client/dom/buffer.js +++ /dev/null @@ -1,135 +0,0 @@ -'use strict'; - -/* global CloudCmd */ -const tryToPromiseAll = require('../../common/try-to-promise-all'); -const Storage = require('./storage'); -const DOM = require('./'); - -module.exports = new BufferProto(); - -function BufferProto() { - const Info = DOM.CurrentInfo; - const CLASS = 'cut-file'; - const COPY = 'copy'; - const CUT = 'cut'; - - const Buffer = { - cut: callIfEnabled.bind(null, cut), - copy: callIfEnabled.bind(null, copy), - clear: callIfEnabled.bind(null, clear), - paste: callIfEnabled.bind(null, paste), - }; - - function showMessage(msg) { - DOM.Dialog.alert(msg); - } - - function getNames() { - const files = DOM.getActiveFiles(); - - return DOM.getFilenames(files); - } - - function addCutClass() { - const files = DOM.getActiveFiles(); - - for (const element of files) { - element.classList.add(CLASS); - } - } - - function rmCutClass() { - const files = DOM.getByClassAll(CLASS); - - for (const element of files) { - element.classList.remove(CLASS); - } - } - - function callIfEnabled(callback) { - const is = CloudCmd.config('buffer'); - - if (is) - return callback(); - - showMessage('Buffer disabled in config!'); - } - - async function readBuffer() { - const [e, cp, ct] = await tryToPromiseAll([ - Storage.getJson(COPY), - Storage.getJson(CUT), - ]); - - return [ - e, - cp, - ct, - ]; - } - - async function copy() { - const names = getNames(); - const from = Info.dirPath; - - await clear(); - - if (!names.length) - return; - - await Storage.remove(CUT); - await Storage.setJson(COPY, { - from, - names, - }); - } - - async function cut() { - const names = getNames(); - const from = Info.dirPath; - - await clear(); - - if (!names.length) - return; - - addCutClass(); - - await Storage.setJson(CUT, { - from, - names, - }); - } - - async function clear() { - await Storage.remove(COPY); - await Storage.remove(CUT); - - rmCutClass(); - } - - async function paste() { - const [error, cp, ct] = await readBuffer(); - - if (error || !cp && !ct) - return showMessage(error || 'Buffer is empty!'); - - const opStr = cp ? 'copy' : 'move'; - const data = cp || ct; - const {Operation} = CloudCmd; - const msg = 'Path is same!'; - const to = Info.dirPath; - - if (data.from === to) - return showMessage(msg); - - Operation.show(opStr, { - ...data, - to, - }); - - await clear(); - } - - return Buffer; -} diff --git a/client/dom/buffer.mjs b/client/dom/buffer.mjs new file mode 100644 index 00000000..01e8142a --- /dev/null +++ b/client/dom/buffer.mjs @@ -0,0 +1,124 @@ +/* global CloudCmd*/ +import tryToPromiseAll from '../../common/try-to-promise-all.js'; +import Storage from './storage.js'; + +const CLASS = 'cut-file'; +const COPY = 'copy'; +const CUT = 'cut'; + +function showMessage(msg) { + globalThis.DOM.Dialog.alert(msg); +} + +function getNames() { + const {DOM} = globalThis; + const files = DOM.getActiveFiles(); + + return DOM.getFilenames(files); +} + +function addCutClass() { + const {DOM} = globalThis; + const files = DOM.getActiveFiles(); + + for (const element of files) { + element.classList.add(CLASS); + } +} + +function rmCutClass() { + const {DOM} = globalThis; + const files = DOM.getByClassAll(CLASS); + + for (const element of files) { + element.classList.remove(CLASS); + } +} + +const checkEnabled = (fn) => () => { + const is = CloudCmd.config('buffer'); + + if (is) + return fn(); + + showMessage('Buffer disabled in config!'); +}; + +async function readBuffer() { + const [e, cp, ct] = await tryToPromiseAll([ + Storage.getJson(COPY), + Storage.getJson(CUT), + ]); + + return [ + e, + cp, + ct, + ]; +} + +export const copy = checkEnabled(async () => { + const Info = globalThis.DOM.CurrentInfo; + const names = getNames(); + const from = Info.dirPath; + + await clear(); + + if (!names.length) + return; + + await Storage.remove(CUT); + await Storage.setJson(COPY, { + from, + names, + }); +}); + +export const cut = checkEnabled(async () => { + const Info = globalThis.DOM.CurrentInfo; + const names = getNames(); + const from = Info.dirPath; + + await clear(); + + if (!names.length) + return; + + addCutClass(); + + await Storage.setJson(CUT, { + from, + names, + }); +}); + +export const clear = checkEnabled(async () => { + await Storage.remove(COPY); + await Storage.remove(CUT); + + rmCutClass(); +}); + +export const paste = checkEnabled(async () => { + const Info = globalThis.DOM.CurrentInfo; + const [error, cp, ct] = await readBuffer(); + + if (error || !cp && !ct) + return showMessage(error || 'Buffer is empty!'); + + const opStr = cp ? 'copy' : 'move'; + const data = cp || ct; + const {Operation} = CloudCmd; + const msg = 'Path is same!'; + const to = Info.dirPath; + + if (data.from === to) + return showMessage(msg); + + Operation.show(opStr, { + ...data, + to, + }); + + await clear(); +}); diff --git a/client/dom/current-file.js b/client/dom/current-file.mjs similarity index 84% rename from client/dom/current-file.js rename to client/dom/current-file.mjs index 963b2a29..e55acbe9 100644 --- a/client/dom/current-file.js +++ b/client/dom/current-file.mjs @@ -1,10 +1,8 @@ -'use strict'; - /* global DOM */ /* global CloudCmd */ -const createElement = require('@cloudcmd/create-element'); -const {encode, decode} = require('../../common/entity'); -const {getTitle, FS} = require('../../common/cloudfunc.mjs'); +import createElement from '@cloudcmd/create-element'; +import {encode, decode} from '../../common/entity.js'; +import {getTitle, FS} from '../../common/cloudfunc.mjs'; let Title; @@ -12,14 +10,15 @@ const CURRENT_FILE = 'current-file'; const encodeNBSP = (a) => a?.replace('\xa0', ' '); const decodeNBSP = (a) => a?.replace(' ', '\xa0'); -module.exports._CURRENT_FILE = CURRENT_FILE; +export const _CURRENT_FILE = CURRENT_FILE; + /** * set name from current (or param) file * * @param name * @param current */ -module.exports.setCurrentName = (name, current) => { +export const setCurrentName = (name, current) => { const Info = DOM.CurrentInfo; const {link} = Info; const {prefix} = CloudCmd; @@ -41,7 +40,7 @@ module.exports.setCurrentName = (name, current) => { * * @param currentFile */ -module.exports.getCurrentName = (currentFile) => { +export const getCurrentName = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); if (!current) @@ -68,18 +67,19 @@ const parseNameAttribute = (attribute) => { return decodeNBSP(decodeURI(atob(attribute))); }; -module.exports._parseNameAttribute = parseNameAttribute; +export const _parseNameAttribute = parseNameAttribute; const parseHrefAttribute = (prefix, attribute) => { attribute = attribute.replace(RegExp('^' + prefix + FS), ''); return decode(decodeNBSP(attribute)); }; -module.exports._parseHrefAttribute = parseHrefAttribute; +export const _parseHrefAttribute = parseHrefAttribute; + /** * get current direcotory path */ -module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => { +export const getCurrentDirPath = (panel = DOM.getPanel()) => { const path = DOM.getByDataName('js-path', panel); return path.textContent; }; @@ -89,7 +89,7 @@ module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => { * * @param currentFile - current file by default */ -module.exports.getCurrentPath = (currentFile) => { +export const getCurrentPath = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const [element] = DOM.getByTag('a', current); const {prefix} = CloudCmd; @@ -100,7 +100,7 @@ module.exports.getCurrentPath = (currentFile) => { /** * get current direcotory name */ -module.exports.getCurrentDirName = () => { +export const getCurrentDirName = () => { const href = DOM .getCurrentDirPath() .replace(/\/$/, ''); @@ -113,7 +113,7 @@ module.exports.getCurrentDirName = () => { /** * get current direcotory path */ -module.exports.getParentDirPath = (panel) => { +export const getParentDirPath = (panel) => { const path = DOM.getCurrentDirPath(panel); const dirName = DOM.getCurrentDirName() + '/'; const index = path.lastIndexOf(dirName); @@ -127,7 +127,7 @@ module.exports.getParentDirPath = (panel) => { /** * get not current direcotory path */ -module.exports.getNotCurrentDirPath = () => { +export const getNotCurrentDirPath = () => { const panel = DOM.getPanel({ active: false, }); @@ -140,14 +140,14 @@ module.exports.getNotCurrentDirPath = () => { * * @currentFile */ -module.exports.getCurrentFile = () => { +export const getCurrentFile = () => { return DOM.getByClass(CURRENT_FILE); }; /** * get current file by name */ -module.exports.getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => { +export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => { const dataName = 'js-file-' + btoa(encodeURI(encodeNBSP(name))); return DOM.getByDataName(dataName, panel); }; @@ -169,7 +169,7 @@ function unsetCurrentFile(currentFile) { /** * unified way to set current file */ -module.exports.setCurrentFile = (currentFile, options) => { +export const setCurrentFile = (currentFile, options) => { const o = options; const currentFileWas = DOM.getCurrentFile(); @@ -216,7 +216,7 @@ module.exports.setCurrentFile = (currentFile, options) => { return DOM; }; -this.setCurrentByName = (name) => { +export const setCurrentByName = (name) => { const current = DOM.getCurrentByName(name); return DOM.setCurrentFile(current); }; @@ -227,7 +227,7 @@ this.setCurrentByName = (name) => { * @param layer - element * @param - position {x, y} */ -module.exports.getCurrentByPosition = ({x, y}) => { +export const getCurrentByPosition = ({x, y}) => { const element = document.elementFromPoint(x, y); const getEl = (el) => { @@ -259,7 +259,7 @@ module.exports.getCurrentByPosition = ({x, y}) => { * * @param currentFile */ -module.exports.isCurrentFile = (currentFile) => { +export const isCurrentFile = (currentFile) => { if (!currentFile) return false; @@ -271,7 +271,7 @@ module.exports.isCurrentFile = (currentFile) => { * * @param name */ -module.exports.setTitle = (name) => { +export const setTitle = (name) => { if (!Title) Title = DOM.getByTag('title')[0] || createElement('title', { innerHTML: name, @@ -288,7 +288,7 @@ module.exports.setTitle = (name) => { * * @param currentFile */ -module.exports.isCurrentIsDir = (currentFile) => { +export const isCurrentIsDir = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const path = DOM.getCurrentPath(current); const fileType = DOM.getCurrentType(current); @@ -299,7 +299,7 @@ module.exports.isCurrentIsDir = (currentFile) => { return isDir || isZip; }; -module.exports.getCurrentType = (currentFile) => { +export const getCurrentType = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const el = DOM.getByDataName('js-type', current); const type = el.className diff --git a/client/dom/current-file.spec.js b/client/dom/current-file.spec.mjs similarity index 97% rename from client/dom/current-file.spec.js rename to client/dom/current-file.spec.mjs index 88d966e7..2a576dce 100644 --- a/client/dom/current-file.spec.js +++ b/client/dom/current-file.spec.mjs @@ -1,10 +1,8 @@ -'use strict'; +import {test, stub} from 'supertape'; +import {create} from 'auto-globals'; +import wraptile from 'wraptile'; +import * as currentFile from './current-file.mjs'; -const {test, stub} = require('supertape'); - -const {create} = require('auto-globals'); -const wraptile = require('wraptile'); -const currentFile = require('./current-file'); const id = (a) => a; const returns = wraptile(id); diff --git a/client/dom/directory.js b/client/dom/directory.js index 6efd989c..bedb7e95 100644 --- a/client/dom/directory.js +++ b/client/dom/directory.js @@ -3,7 +3,7 @@ /* global CloudCmd */ const philip = require('philip'); -const Images = require('./images'); +const Images = require('./images.mjs'); const {FS} = require('../../common/cloudfunc.mjs'); const DOM = require('.'); const Dialog = require('./dialog'); diff --git a/client/dom/images.js b/client/dom/images.mjs similarity index 84% rename from client/dom/images.js rename to client/dom/images.mjs index bb5579c1..9682e666 100644 --- a/client/dom/images.js +++ b/client/dom/images.mjs @@ -1,10 +1,5 @@ /* global DOM */ - -'use strict'; - -const createElement = require('@cloudcmd/create-element'); - -const Images = module.exports; +import createElement from '@cloudcmd/create-element'; const LOADING = 'loading'; const HIDDEN = 'hidden'; @@ -12,7 +7,8 @@ const ERROR = 'error'; const getLoadingType = () => isSVG() ? '-svg' : '-gif'; -module.exports.get = getElement; +export const get = getElement; + /** * check SVG SMIL animation support */ @@ -40,7 +36,7 @@ function getElement() { } /* Функция создаёт картинку загрузки */ -module.exports.loading = () => { +export const loading = () => { const element = getElement(); const {classList} = element; const loadingImage = LOADING + getLoadingType(); @@ -52,7 +48,7 @@ module.exports.loading = () => { }; /* Функция создаёт картинку ошибки загрузки */ -module.exports.error = () => { +export const error = () => { const element = getElement(); const {classList} = element; const loadingImage = LOADING + getLoadingType(); @@ -63,14 +59,21 @@ module.exports.error = () => { return element; }; -module.exports.show = show; -module.exports.show.load = show; -module.exports.show.error = error; +show.load = show; +show.error = (text) => { + const image = Images.error(); + + DOM.show(image); + image.title = text; + + return image; +}; + /** * Function shows loading spinner * position = {top: true}; */ -function show(position, panel) { +export function show(position, panel) { const image = Images.loading(); const parent = image.parentElement; const refreshButton = DOM.getRefreshButton(panel); @@ -96,19 +99,10 @@ function show(position, panel) { return image; } -function error(text) { - const image = Images.error(); - - DOM.show(image); - image.title = text; - - return image; -} - /** * hide load image */ -module.exports.hide = () => { +export const hide = () => { const element = Images.get(); DOM.hide(element); @@ -116,7 +110,7 @@ module.exports.hide = () => { return Images; }; -module.exports.setProgress = (value, title) => { +export const setProgress = (value, title) => { const DATA = 'data-progress'; const element = Images.get(); @@ -131,7 +125,7 @@ module.exports.setProgress = (value, title) => { return Images; }; -module.exports.clearProgress = () => { +export const clearProgress = () => { const DATA = 'data-progress'; const element = Images.get(); @@ -143,3 +137,13 @@ module.exports.clearProgress = () => { return Images; }; + +const Images = { + clearProgress, + setProgress, + show, + hide, + get, + error, + loading, +}; diff --git a/client/dom/index.js b/client/dom/index.js index b6f128e7..27a1a5c6 100644 --- a/client/dom/index.js +++ b/client/dom/index.js @@ -3,12 +3,12 @@ /* global CloudCmd */ const Util = require('../../common/util'); -const Images = require('./images'); +const Images = require('./images.mjs'); const RESTful = require('./rest'); const Storage = require('./storage'); const renameCurrent = require('./operations/rename-current'); -const CurrentFile = require('./current-file'); +const CurrentFile = require('./current-file.mjs'); const DOMTree = require('./dom-tree'); const Cmd = module.exports; @@ -32,7 +32,7 @@ DOM.CurrentInfo = CurrentInfo; module.exports = DOM; DOM.uploadDirectory = require('./directory'); -DOM.Buffer = require('./buffer'); +DOM.Buffer = require('./buffer.mjs'); DOM.Events = require('#dom/events'); const loadRemote = require('./load-remote'); diff --git a/client/dom/io/send-request.js b/client/dom/io/send-request.js index bc52d667..c61544f1 100644 --- a/client/dom/io/send-request.js +++ b/client/dom/io/send-request.js @@ -3,7 +3,7 @@ /* global CloudCmd */ const {promisify} = require('es6-promisify'); -const Images = require('../images'); +const Images = require('../images.mjs'); const load = require('../load'); module.exports = promisify((params, callback) => { diff --git a/client/dom/load.js b/client/dom/load.js index 03c25d73..d060a92c 100644 --- a/client/dom/load.js +++ b/client/dom/load.js @@ -4,7 +4,7 @@ const itype = require('itype'); const jonny = require('jonny'); const Emitify = require('emitify'); const exec = require('execon'); -const Images = require('./images'); +const Images = require('./images.mjs'); module.exports.getIdBySrc = getIdBySrc; /** diff --git a/client/dom/operations/rename-current.js b/client/dom/operations/rename-current.js index 1293dc33..ab658edf 100644 --- a/client/dom/operations/rename-current.js +++ b/client/dom/operations/rename-current.js @@ -7,7 +7,7 @@ const _Dialog = require('../dialog'); const Storage = require('../storage'); const RESTful = require('../rest'); -const _currentFile = require('../current-file'); +const _currentFile = require('../current-file.mjs'); module.exports = async (current, overrides = {}) => { const { diff --git a/client/dom/rest.js b/client/dom/rest.js index 444fce15..7596f620 100644 --- a/client/dom/rest.js +++ b/client/dom/rest.js @@ -4,7 +4,7 @@ const {tryToCatch} = require('try-to-catch'); const {encode} = require('../../common/entity'); -const Images = require('./images'); +const Images = require('./images.mjs'); const IO = require('./io'); const Dialog = require('./dialog'); diff --git a/client/dom/upload-files.js b/client/dom/upload-files.js index 1a64fb6c..a1206282 100644 --- a/client/dom/upload-files.js +++ b/client/dom/upload-files.js @@ -5,7 +5,7 @@ const {eachSeries} = require('execon'); const wraptile = require('wraptile'); const load = require('./load'); -const Images = require('./images'); +const Images = require('./images.mjs'); const {alert} = require('./dialog'); const {FS} = require('../../common/cloudfunc.mjs'); diff --git a/client/key/index.js b/client/key/index.mjs similarity index 96% rename from client/key/index.js rename to client/key/index.mjs index 5c1fa2b7..64fd1bc3 100644 --- a/client/key/index.js +++ b/client/key/index.mjs @@ -1,16 +1,12 @@ -'use strict'; - /* global CloudCmd, DOM */ -const clipboard = require('@cloudcmd/clipboard'); -const {fullstore} = require('fullstore'); - -const Buffer = require('../dom/buffer'); -const Events = require('#dom/events'); -const KEY = require('./key'); - -const _vim = require('./vim'); -const setCurrentByChar = require('./set-current-by-char'); -const {createBinder} = require('./binder'); +import clipboard from '@cloudcmd/clipboard'; +import {fullstore} from 'fullstore'; +import * as Events from '#dom/events'; +import * as Buffer from '../dom/buffer.mjs'; +import KEY from './key.js'; +import _vim from './vim/index.js'; +import setCurrentByChar from './set-current-by-char.js'; +import {createBinder} from './binder.js'; const Chars = fullstore(); @@ -28,13 +24,16 @@ Chars([]); const {assign} = Object; const binder = createBinder(); -module.exports = assign(binder, KEY); -module.exports.bind = () => { +const bind = () => { Events.addKey(listener, true); binder.setBind(); }; -module.exports._listener = listener; +export const Key = assign(binder, KEY, { + bind, +}); + +export const _listener = listener; function getChar(event) { /* diff --git a/client/key/index.spec.js b/client/key/index.spec.js index 3e52e5ac..d8167079 100644 --- a/client/key/index.spec.js +++ b/client/key/index.spec.js @@ -7,7 +7,8 @@ const supertape = require('supertape'); const {ESC} = require('./key'); -const {_listener, setBind} = require('.'); +const {Key, _listener} = require('./index.mjs'); + const {getDOM, getCloudCmd} = require('./vim/globals.fixture'); const test = autoGlobals(supertape); const {stub} = supertape; @@ -26,7 +27,7 @@ test('cloudcmd: client: key: enable vim', async (t) => { altKey: false, }; - setBind(); + Key.setBind(); await _listener(event, { vim, @@ -48,7 +49,7 @@ test('cloudcmd: client: key: disable vim', async (t) => { altKey: false, }; - setBind(); + Key.setBind(); await _listener(event, { config, _config, diff --git a/client/key/vim/index.js b/client/key/vim/index.js index a38e9970..ddfe49c3 100644 --- a/client/key/vim/index.js +++ b/client/key/vim/index.js @@ -21,6 +21,7 @@ module.exports = (key, event, overrides = {}) => { }; const operations = getOperations(event, deps); + vim(key, operations, deps); }; @@ -38,9 +39,13 @@ const getOperations = (event, deps) => { toggleSelectedFile, Buffer = {}, + createFindNext = _createFindNext, } = deps; return { + findNext: createFindNext({ + setCurrentByName, + }), escape: unselectFiles, remove: () => { @@ -118,11 +123,6 @@ const getOperations = (event, deps) => { setCurrentByName(result); }, - findNext: () => { - const name = finder.findNext(); - setCurrentByName(name); - }, - findPrevious: () => { const name = finder.findPrevious(); setCurrentByName(name); @@ -131,3 +131,10 @@ const getOperations = (event, deps) => { }; module.exports.selectFile = selectFileNotParent; + +const _createFindNext = (overrides = {}) => () => { + const {setCurrentByName} = overrides; + const name = finder.findNext(); + + setCurrentByName(name); +}; diff --git a/client/key/vim/index.spec.js b/client/key/vim/index.spec.js index ee7fabe0..6fcad11e 100644 --- a/client/key/vim/index.spec.js +++ b/client/key/vim/index.spec.js @@ -570,17 +570,13 @@ test('cloudcmd: client: find', (t) => { test('cloudcmd: client: key: n', (t) => { const findNext = stub(); + const createFindNext = stub().returns(findNext); - mockRequire(pathFind, { - findNext, - }); - - const vim = reRequire(pathVim); const event = {}; - vim('n', event); - - stopAll(); + vim('n', event, { + createFindNext, + }); t.calledWithNoArgs(findNext, 'should call findNext'); t.end(); @@ -664,7 +660,7 @@ test('cloudcmd: client: vim: terminal', (t) => { t.end(); }); -test.skip('cloudcmd: client: vim: edit', async (t) => { +test('cloudcmd: client: vim: edit', async (t) => { globalThis.DOM = getDOM(); globalThis.CloudCmd = getCloudCmd(); diff --git a/client/modules/cloud.js b/client/modules/cloud.js index 81498c4b..53a6d9e9 100644 --- a/client/modules/cloud.js +++ b/client/modules/cloud.js @@ -9,7 +9,7 @@ const load = require('load.js'); const {ajax} = require('../dom/load'); const Files = require('../dom/files'); -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); const {log} = CloudCmd; const upload = currify(_upload); diff --git a/client/modules/config/index.js b/client/modules/config/index.js index 4bb4bd5b..db778cbc 100644 --- a/client/modules/config/index.js +++ b/client/modules/config/index.js @@ -13,7 +13,7 @@ const load = require('load.js'); const createElement = require('@cloudcmd/create-element'); const input = require('./input'); -const Images = require('../../dom/images'); +const Images = require('../../dom/images.mjs'); const Events = require('#dom/events'); const Files = require('../../dom/files'); diff --git a/client/modules/contact.js b/client/modules/contact.js index 76a07d30..c6266de0 100644 --- a/client/modules/contact.js +++ b/client/modules/contact.js @@ -6,7 +6,7 @@ CloudCmd.Contact = exports; const olark = require('@cloudcmd/olark'); -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); const {Events} = DOM; const {Key} = CloudCmd; diff --git a/client/modules/help.js b/client/modules/help.js index 785bb32c..242b7c16 100644 --- a/client/modules/help.js +++ b/client/modules/help.js @@ -3,7 +3,7 @@ /* global CloudCmd */ CloudCmd.Help = exports; -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); module.exports.init = () => { Images.show.load('top'); diff --git a/client/modules/konsole.js b/client/modules/konsole.js index 3de160fd..ae5bc42c 100644 --- a/client/modules/konsole.js +++ b/client/modules/konsole.js @@ -12,7 +12,7 @@ const {tryToCatch} = require('try-to-catch'); const loadJS = require('load.js').js; const createElement = require('@cloudcmd/create-element'); -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); const {Dialog, CurrentInfo: Info} = DOM; const rmLastSlash = (a) => a.replace(/\/$/, '') || '/'; diff --git a/client/modules/markdown.js b/client/modules/markdown.js index 6c5c3282..9dc224af 100644 --- a/client/modules/markdown.js +++ b/client/modules/markdown.js @@ -5,7 +5,7 @@ CloudCmd.Markdown = exports; const createElement = require('@cloudcmd/create-element'); -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); const {Markdown} = require('../dom/rest'); const {alert} = require('../dom/dialog'); diff --git a/client/modules/operation/index.js b/client/modules/operation/index.mjs similarity index 93% rename from client/modules/operation/index.js rename to client/modules/operation/index.mjs index 3cce60d8..8e8149e5 100644 --- a/client/modules/operation/index.js +++ b/client/modules/operation/index.mjs @@ -1,28 +1,20 @@ -/* global CloudCmd */ -/* global Util */ -/* global DOM */ -/* global fileop */ +import currify from 'currify'; +import wraptile from 'wraptile'; +import {promisify} from 'es6-promisify'; +import exec from 'execon'; +import load from 'load.js'; +import {tryToCatch} from 'try-to-catch'; +import {encode} from '../../../common/entity.js'; +import removeExtension from './remove-extension.js'; +import {setListeners} from './set-listeners.mjs'; +import getNextCurrentName from './get-next-current-name.js'; -'use strict'; - -const currify = require('currify'); -const wraptile = require('wraptile'); -const {promisify} = require('es6-promisify'); -const exec = require('execon'); -const load = require('load.js'); -const {tryToCatch} = require('try-to-catch'); - -const {encode} = require('../../../common/entity'); -const removeExtension = require('./remove-extension'); -const setListeners = require('./set-listeners'); -const getNextCurrentName = require('./get-next-current-name'); +const {DOM, CloudCmd} = globalThis; const removeQuery = (a) => a.replace(/\?.*/, ''); const Name = 'Operation'; -CloudCmd[Name] = exports; - const {config} = CloudCmd; const {Dialog, Images} = DOM; @@ -53,7 +45,7 @@ const noFilesCheck = () => { return is; }; -module.exports.init = promisify((callback) => { +export const init = promisify((callback) => { showLoad(); exec.series([ @@ -92,7 +84,7 @@ const onConnect = currify((fn, operator) => { async function initOperations(prefix, socketPrefix, fn) { socketPrefix = `${socketPrefix}/fileop`; - const operator = await fileop({ + const operator = await globalThis.fileop({ prefix, socketPrefix, }); @@ -198,11 +190,11 @@ function getPacker(type) { return packTarFn; } -module.exports.hide = () => { +export const hide = () => { CloudCmd.View.hide(); }; -module.exports.show = (operation, data) => { +export const show = (operation, data) => { if (!Loaded) return; @@ -505,8 +497,14 @@ async function prompt(msg, to, names) { return await Dialog.prompt(msg, to); } +globalThis.CloudCmd[Name] = { + init, + hide, + show, +}; + async function loadAll() { - const {prefix} = CloudCmd; + const {prefix} = globalThis.CloudCmd; const file = `${prefix}/fileop/fileop.js`; const [error] = await tryToCatch(load.js, file); diff --git a/client/modules/operation/set-listeners.js b/client/modules/operation/set-listeners.mjs similarity index 85% rename from client/modules/operation/set-listeners.js rename to client/modules/operation/set-listeners.mjs index 495cd04b..d5052cff 100644 --- a/client/modules/operation/set-listeners.js +++ b/client/modules/operation/set-listeners.mjs @@ -1,14 +1,11 @@ -'use strict'; - /* global DOM */ -const forEachKey = require('for-each-key'); - -const wraptile = require('wraptile'); -const format = require('./format'); +import forEachKey from 'for-each-key'; +import wraptile from 'wraptile'; +import format from './format.js'; const {Dialog, Images} = DOM; -module.exports = (options) => (emitter) => { +export const setListeners = (options) => (emitter) => { const { operation, callback, @@ -43,10 +40,13 @@ module.exports = (options) => (emitter) => { operation, })); + let noProgress = true; + const listeners = { progress: (value) => { done = value === 100; progress.setProgress(value); + noProgress = false; }, end: () => { @@ -54,7 +54,7 @@ module.exports = (options) => (emitter) => { forEachKey(removeListener, listeners); progress.remove(); - if (lastError || done) + if (lastError || done || noProgress) callback(); }, diff --git a/client/modules/terminal-run.js b/client/modules/terminal-run.js index 33b5dbae..4dcf24ac 100644 --- a/client/modules/terminal-run.js +++ b/client/modules/terminal-run.js @@ -10,7 +10,7 @@ require('../../css/terminal.css'); const exec = require('execon'); const load = require('load.js'); const DOM = require('../dom'); -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); const {Dialog} = DOM; const {Key, config} = CloudCmd; diff --git a/client/modules/terminal.js b/client/modules/terminal.js index 7b9197ca..6a355560 100644 --- a/client/modules/terminal.js +++ b/client/modules/terminal.js @@ -9,7 +9,7 @@ require('../../css/terminal.css'); const exec = require('execon'); const load = require('load.js'); const DOM = require('../dom'); -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); const loadParallel = load.parallel; diff --git a/client/modules/upload.js b/client/modules/upload.js index 63a2cd0f..23fbdaf5 100644 --- a/client/modules/upload.js +++ b/client/modules/upload.js @@ -6,7 +6,7 @@ CloudCmd.Upload = exports; const createElement = require('@cloudcmd/create-element'); const Files = require('../dom/files'); -const Images = require('../dom/images'); +const Images = require('../dom/images.mjs'); const uploadFiles = require('../dom/upload-files'); module.exports.init = async () => { diff --git a/client/modules/user-menu/index.js b/client/modules/user-menu/index.js index 713bdb8a..1dba5d23 100644 --- a/client/modules/user-menu/index.js +++ b/client/modules/user-menu/index.js @@ -12,7 +12,7 @@ const {tryCatch} = require('try-catch'); const {tryToCatch} = require('try-to-catch'); const {codeFrameColumns} = require('@babel/code-frame'); -const Images = require('../../dom/images'); +const Images = require('../../dom/images.mjs'); const Dialog = require('../../dom/dialog'); const getUserMenu = require('./get-user-menu'); const navigate = require('./navigate'); diff --git a/client/modules/view/index.js b/client/modules/view/index.js index 2aa3e0c7..89d68ee4 100644 --- a/client/modules/view/index.js +++ b/client/modules/view/index.js @@ -27,7 +27,7 @@ const { const Files = require('../../dom/files'); const Events = require('#dom/events'); -const Images = require('../../dom/images'); +const Images = require('../../dom/images.mjs'); const {encode} = require('../../../common/entity'); const isString = (a) => typeof a === 'string'; diff --git a/eslint.config.mjs b/eslint.config.mjs index d445c4a9..cf7a6a2d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -33,9 +33,7 @@ export default defineConfig([ }, { files: ['{client,common,static}/**/*.js'], languageOptions: { - globals: { - ...globals.browser, - }, + globals: globals.browser, }, }, ...matchToFlat(match), diff --git a/package.json b/package.json index 9b3034e6..c9760653 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "cloudcmd", - "version": "19.1.7", + "version": "19.1.9", + "type": "commonjs", "author": "coderaiser (https://github.com/coderaiser)", "description": "File manager for the web with console and editor", "homepage": "http://cloudcmd.io", @@ -157,7 +158,7 @@ "@cloudcmd/olark": "^3.0.2", "@cloudcmd/stub": "^5.0.0", "@iocmd/wait": "^2.1.0", - "@putout/eslint-flat": "^3.0.1", + "@putout/eslint-flat": "^4.0.0", "@putout/plugin-cloudcmd": "^4.0.0", "@types/node-fetch": "^2.6.11", "auto-globals": "^4.0.0", @@ -174,7 +175,7 @@ "emitify": "^4.0.1", "eslint": "^9.23.0", "eslint-plugin-n": "^17.0.0-4", - "eslint-plugin-putout": "^29.0.2", + "eslint-plugin-putout": "^30.0.0", "globals": "^17.0.0", "gritty": "^9.0.0", "gunzip-maybe": "^1.3.1", @@ -218,9 +219,7 @@ "webpackbar": "^7.0.0" }, "imports": { - "#dom/events": { - "default": "./client/dom/events/index.mjs" - } + "#dom/events": "./client/dom/events/index.mjs" }, "engines": { "node": ">=22"