diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..13d9e227 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,38 @@ +'use strict'; + +module.exports = { + extends: [ + 'plugin:putout/safe+align', + ], + plugins: [ + 'putout', + 'n', + ], + rules: { + 'key-spacing': 'off', + 'n/prefer-node-protocol': 'error', + }, + overrides: [{ + files: ['bin/release.js'], + rules: { + 'no-console': 'off', + 'n/shebang': 'off', + }, + }, { + files: ['client/dom/index.js'], + rules: { + 'no-multi-spaces': 'off', + }, + }, { + files: ['bin/cloudcmd.js'], + rules: { + 'no-console': 'off', + }, + }, { + files: ['{client,common,static}/**/*.js'], + env: { + browser: true, + }, + }], + ignorePatterns: ['*.md{js}'], +}; diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 376b0f08..e4877cb7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -11,12 +11,12 @@ jobs: packages: write steps: - name: Checkout - uses: actions/checkout@v5 - - uses: oven-sh/setup-bun@v2 + uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 with: bun-version: latest - name: Use Node.js 22.x - uses: actions/setup-node@v6 + uses: actions/setup-node@v4 with: node-version: 22.x - name: Install Redrun diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 1b392900..f81c19a5 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -9,16 +9,16 @@ jobs: strategy: matrix: node-version: + - 20.x - 22.x - 24.x - - 25.x steps: - - uses: actions/checkout@v5 - - uses: oven-sh/setup-bun@v2 + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 with: bun-version: latest - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - name: Install Redrun diff --git a/.npmignore b/.npmignore index b668cf7f..08137773 100644 --- a/.npmignore +++ b/.npmignore @@ -18,7 +18,6 @@ now.json app.json bower.json manifest.yml -deno.json bin/release.mjs diff --git a/.putout.json b/.putout.json index b868f59d..239eb08d 100644 --- a/.putout.json +++ b/.putout.json @@ -7,9 +7,6 @@ "fontello.json", "*.md" ], - "rules": { - "package-json/add-type": "off" - }, "match": { "base64": { "types/convert-typeof-to-is-type": "off" @@ -29,12 +26,9 @@ "server/{server,exit,terminal,distribute/log}.{js,mjs}": { "remove-console": "off" }, - "client/{client,cloudcmd,load-module}.{js,mjs}": { + "client/{client,cloudcmd,load-module}.js": { "remove-console": "off" }, - "client": { - "nodejs": "off" - }, "client/sw": { "remove-console": "off" }, @@ -49,9 +43,6 @@ }, "vim.js": { "merge-duplicate-functions": "off" - }, - "common": { - "nodejs/declare": "off" } } } diff --git a/.webpack/css.mjs b/.webpack/css.js similarity index 71% rename from .webpack/css.mjs rename to .webpack/css.js index 338ae91b..2d3faf55 100644 --- a/.webpack/css.mjs +++ b/.webpack/css.js @@ -1,6 +1,9 @@ -import {env} from 'node:process'; -import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin'; -import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +'use strict'; + +const {env} = require('node:process'); + +const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const isDev = env.NODE_ENV === 'development'; @@ -26,7 +29,7 @@ const rules = [{ type: 'asset/inline', }]; -export default { +module.exports = { plugins, module: { rules, diff --git a/.webpack/html.mjs b/.webpack/html.js similarity index 85% rename from .webpack/html.mjs rename to .webpack/html.js index e90038ac..3717bcc9 100644 --- a/.webpack/html.mjs +++ b/.webpack/html.js @@ -1,9 +1,11 @@ -import {env} from 'node:process'; -import HtmlWebpackPlugin from 'html-webpack-plugin'; +'use strict'; + +const {env} = require('node:process'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); const isDev = env.NODE_ENV === 'development'; -export const plugins = [ +const plugins = [ new HtmlWebpackPlugin({ inject: false, template: 'html/index.html', @@ -11,6 +13,10 @@ export const plugins = [ }), ]; +module.exports = { + plugins, +}; + function getMinifyHtmlOptions() { return { removeComments: true, diff --git a/.webpack/js.mjs b/.webpack/js.js similarity index 90% rename from .webpack/js.mjs rename to .webpack/js.js index d52e0388..d666780d 100644 --- a/.webpack/js.mjs +++ b/.webpack/js.js @@ -1,12 +1,18 @@ -import {resolve, sep} from 'node:path'; -import {env} from 'node:process'; -import webpack from 'webpack'; -import WebpackBar from 'webpackbar'; +'use strict'; +const { + resolve, + sep, + join, +} = require('node:path'); + +const {env} = require('node:process'); const { EnvironmentPlugin, NormalModuleReplacementPlugin, -} = webpack; +} = require('webpack'); + +const WebpackBar = require('webpackbar'); const modules = './modules'; const dirModules = './client/modules'; @@ -17,7 +23,7 @@ const dir = './client'; const {NODE_ENV} = env; const isDev = NODE_ENV === 'development'; -const rootDir = new URL('..', import.meta.url).pathname; +const rootDir = join(__dirname, '..'); const dist = resolve(rootDir, 'dist'); const distDev = resolve(rootDir, 'dist-dev'); const devtool = isDev ? 'eval' : 'source-map'; @@ -25,7 +31,7 @@ const devtool = isDev ? 'eval' : 'source-map'; const notEmpty = (a) => a; const clean = (array) => array.filter(notEmpty); -const noParse = (a) => a.endsWith('.spec.js'); +const noParse = (a) => /\.spec\.js$/.test(a); const options = { babelrc: true, }; @@ -86,7 +92,7 @@ const splitChunks = { }, }; -export default { +module.exports = { resolve: { symlinks: false, alias: { @@ -94,8 +100,8 @@ export default { 'node:path': 'path', }, fallback: { - path: import.meta.resolve('path-browserify'), - process: import.meta.resolve('process/browser'), + path: require.resolve('path-browserify'), + process: require.resolve('process/browser'), }, }, devtool, @@ -114,7 +120,7 @@ export default { 'terminal': `${dirCss}/terminal.css`, 'user-menu': `${dirCss}/user-menu.css`, 'sw': `${dir}/sw/sw.js`, - 'cloudcmd': `${dir}/cloudcmd.mjs`, + 'cloudcmd': `${dir}/cloudcmd.js`, [`${modules}/edit`]: `${dirModules}/edit.js`, [`${modules}/edit-file`]: `${dirModules}/edit-file.js`, [`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`, @@ -127,7 +133,7 @@ export default { [`${modules}/config`]: `${dirModules}/config/index.js`, [`${modules}/contact`]: `${dirModules}/contact.js`, [`${modules}/upload`]: `${dirModules}/upload.js`, - [`${modules}/operation`]: `${dirModules}/operation/index.mjs`, + [`${modules}/operation`]: `${dirModules}/operation/index.js`, [`${modules}/konsole`]: `${dirModules}/konsole.js`, [`${modules}/terminal`]: `${dirModules}/terminal.js`, [`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`, diff --git a/ChangeLog b/ChangeLog index edf5330e..046c9ef0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,138 +1,3 @@ -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: -- 23a6a698 client: dom/events -> #dom/events -- 9cebb241 client: dom: events: migrate to ESM -- a94fa0d4 client: cloudcmd: migrate to ESM -- 3bdf47a5 client: migrate to ESM - -2026.01.16, v19.1.6 - -fix: -- a523ef65 tests - -feature: -- 64654e8d common: cloudfunc: migrate to ESM -- add31607 common: cloudfunc: get rid of bas64 -- e36de00c modulas: migrate to ESM - -2026.01.16, v19.1.5 - -feature: -- 450f1461 client: improve testability -- d979e949 server: env: migrate to ESM - -2026.01.15, v19.1.4 - -feature: -- 6e778a35 client: sort: migrate to ESM -- e27ef51d client: sort: migrate to ESM -- 917f5851 client: load-module: migrate to ESM -- 9950caca client: get-json-from-file-table: migrate to ESM - -2026.01.15, v19.1.3 - -feature: -- f903c5c9 cloudcmd: multi-rename v3.0.0 - -2026.01.14, v19.1.2 - -fix: -- 9e2c5ac6 client: edit-names: group rename not renaming (#453) -- f0dcbe94 client: key: config - -feature: -- 6856207d server: env -> env.parse -- dc99417c client: key: get rid of mock-require -- 4bb7d704 client: modules: view: get rid of mock-require - -2026.01.12, v19.1.1 - -feature: -- 5cc6f79d cloudcmd: @cloudcmd/stub v5.0.0 -- 024bc413 cloudcmd: fullstore v4.0.0 -- 53f6f9e7 cloudcmd: globals v17.0.0 -- 6d21c539 cloudcmd: madrun v12.1.0 -- 253389ea cloudcmd: supertape v12.0.0 - -2025.12.31, v19.1.0 - -feature: -- 0ff16314 cloudcmd: redlint v5.0.0 -- 43edba8c cloudcmd: try-to-catch v4.0.0 -- 06f3b782 cloudcmd: try-catch v4.0.4 -- dfcd6557 deno config: add -- ab20a462 server: bun support (oven-sh/bun#25674) - -2025.12.24, v19.0.17 - -feature: -- 0222d177 cloudcmd: gritty v9.0.0 - -2025.12.05, v19.0.16 - -feature: -- 14ec19e8 cloudcmd: find-up v8.0.0 -- e6a00979 cloudcmd: eslint-plugin-putout v29.0.2 -- 5b5352c7 cloudcmd: putout v41.0.0 - -2025.11.28, v19.0.15 - -feature: -- 00676531 cloudcmd: aleman v1.16.5 - -2025.11.27, v19.0.14 - -fix: -- 2a525e9b aleman: copy paste in text editor (#449) - -feature: -- 3ceb9a8c cloudcmd: open v11.0.0 - -2025.09.26, v19.0.13 - -feature: -- 8477f3e4 cloudcmd: aleman v1.16.3 (#446) - -2025.09.25, v19.0.12 - -feature: -- 836e908e cloudcmd: aleman v1.16.2 - -2025.09.24, v19.0.11 - -feature: -- f4386a6f cloudcmd: aleman v1.16.1 - -2025.09.23, v19.0.10 - -feature: -- 2e667ba6 cloudcmd: aleman v1.15.0 - -2025.09.22, v19.0.9 - -feature: -- 60c56164 cloudcmd: aleman v1.14.4 - 2025.09.20, v19.0.8 feature: diff --git a/HELP.md b/HELP.md index 68087875..45f3bbd8 100644 --- a/HELP.md +++ b/HELP.md @@ -1,4 +1,4 @@ -# Cloud Commander v19.1.9 +# Cloud Commander v19.0.8 ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] @@ -1111,25 +1111,6 @@ 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)** -- *2026.01.15*, **[v19.1.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.4)** -- *2026.01.15*, **[v19.1.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.3)** -- *2026.01.14*, **[v19.1.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.2)** -- *2026.01.12*, **[v19.1.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.1)** -- *2025.12.31*, **[v19.1.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.0)** -- *2025.12.24*, **[v19.0.17](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.17)** -- *2025.12.05*, **[v19.0.16](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.16)** -- *2025.11.28*, **[v19.0.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.15)** -- *2025.11.27*, **[v19.0.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.14)** -- *2025.09.26*, **[v19.0.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.13)** -- *2025.09.25*, **[v19.0.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.12)** -- *2025.09.24*, **[v19.0.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.11)** -- *2025.09.23*, **[v19.0.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.10)** -- *2025.09.22*, **[v19.0.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.9)** - *2025.09.20*, **[v19.0.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.8)** - *2025.09.18*, **[v19.0.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.7)** - *2025.09.17*, **[v19.0.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.6)** diff --git a/README.md b/README.md index 8e510161..b3ff3208 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cloud Commander v19.1.9 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] +# Cloud Commander v19.0.8 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] diff --git a/bin/cloudcmd.mjs b/bin/cloudcmd.mjs index e1c358ed..11dabd3d 100755 --- a/bin/cloudcmd.mjs +++ b/bin/cloudcmd.mjs @@ -3,12 +3,12 @@ import process from 'node:process'; import {createRequire} from 'node:module'; import {promisify} from 'node:util'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import {createSimport} from 'simport'; import parse from 'yargs-parser'; import exit from '../server/exit.js'; -import {createConfig, configPath} from '../server/config.mjs'; -import * as env from '../server/env.mjs'; +import {createConfig, configPath} from '../server/config.js'; +import env from '../server/env.js'; import prefixer from '../server/prefixer.js'; import * as validate from '../server/validate.mjs'; @@ -101,27 +101,27 @@ const yargsOptions = { ], default: { 'server': true, - 'name': choose(env.parse('name'), config('name')), + 'name': choose(env('name'), config('name')), 'auth': choose(env.bool('auth'), config('auth')), 'port': config('port'), 'online': config('online'), 'open': choose(env.bool('open'), config('open')), - 'editor': env.parse('editor') || config('editor'), - 'menu': env.parse('menu') || config('menu'), + 'editor': env('editor') || config('editor'), + 'menu': env('menu') || config('menu'), 'packer': config('packer') || 'tar', 'zip': config('zip'), - 'username': env.parse('username') || config('username'), - 'root': choose(env.parse('root'), config('root')), - 'prefix': choose(env.parse('prefix'), config('prefix')), + 'username': env('username') || config('username'), + 'root': choose(env('root'), config('root')), + 'prefix': choose(env('prefix'), config('prefix')), 'console': choose(env.bool('console'), config('console')), 'contact': choose(env.bool('contact'), config('contact')), 'terminal': choose(env.bool('terminal'), config('terminal')), - 'columns': env.parse('columns') || config('columns') || '', - 'theme': env.parse('theme') || config('theme') || '', + 'columns': env('columns') || config('columns') || '', + 'theme': env('theme') || config('theme') || '', 'vim': choose(env.bool('vim'), config('vim')), 'log': config('log'), - 'import-url': env.parse('import_url') || config('importUrl'), + 'import-url': env('import_url') || config('importUrl'), 'import-listen': choose(env.bool('import_listen'), config('importListen')), 'import': choose(env.bool('import'), config('import')), 'export': choose(env.bool('export'), config('export')), @@ -132,15 +132,15 @@ const yargsOptions = { 'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')), 'config-dialog': choose(env.bool('config_dialog'), config('configDialog')), 'config-auth': choose(env.bool('config_auth'), config('configAuth')), - 'terminal-path': env.parse('terminal_path') || config('terminalPath'), - 'terminal-command': env.parse('terminal_command') || config('terminalCommand'), + 'terminal-path': env('terminal_path') || config('terminalPath'), + 'terminal-command': env('terminal_command') || config('terminalCommand'), 'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')), 'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')), 'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')), 'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')), 'keys-panel': env.bool('keys_panel') || config('keysPanel'), - 'import-token': env.parse('import_token') || config('importToken'), - 'export-token': env.parse('export_token') || config('exportToken'), + 'import-token': env('import_token') || config('importToken'), + 'export-token': env('export_token') || config('exportToken'), 'dropbox': config('dropbox'), 'dropbox-token': config('dropboxToken') || '', @@ -238,7 +238,7 @@ async function main() { menu: config('menu'), }; - const password = env.parse('password') || args.password; + const password = env('password') || args.password; if (password) config('password', await getPassword(password)); diff --git a/bin/release.mjs b/bin/release.mjs index 004b908e..694eb7da 100755 --- a/bin/release.mjs +++ b/bin/release.mjs @@ -2,7 +2,7 @@ import {promisify} from 'node:util'; import process from 'node:process'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import {createSimport} from 'simport'; import minor from 'minor'; import _place from 'place'; diff --git a/client/client.mjs b/client/client.js similarity index 92% rename from client/client.mjs rename to client/client.js index b6c3a436..df121fb6 100644 --- a/client/client.mjs +++ b/client/client.js @@ -1,24 +1,30 @@ -import process from 'node:process'; +'use strict'; + +const process = require('node:process'); /* global DOM */ -import Emitify from 'emitify'; -import inherits from 'inherits'; -import rendy from 'rendy'; -import load from 'load.js'; -import {tryToCatch} from 'try-to-catch'; -import {addSlashToEnd} from 'format-io'; -import pascalCase from 'just-pascal-case'; -import currify from 'currify'; -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.mjs'; -import { +const Emitify = require('emitify'); +const inherits = require('inherits'); +const rendy = require('rendy'); +const load = require('load.js'); +const tryToCatch = require('try-to-catch'); +const {addSlashToEnd} = require('format-io'); +const pascalCase = require('just-pascal-case'); +const currify = require('currify'); + +const Images = require('./dom/images'); + +const {unregisterSW} = require('./sw/register'); +const getJsonFromFileTable = require('./get-json-from-file-table'); +const Key = require('./key'); + +const { apiURL, formatMsg, buildFromJSON, -} from '../common/cloudfunc.mjs'; -import {loadModule} from './load-module.mjs'; +} = require('../common/cloudfunc'); + +const loadModule = require('./load-module'); const noJS = (a) => a.replace(/.js$/, ''); @@ -26,19 +32,16 @@ const isDev = process.env.NODE_ENV === 'development'; inherits(CloudCmdProto, Emitify); -export const createCloudCmd = ({DOM, Listeners}) => { - return new CloudCmdProto({ - DOM, - Listeners, - }); -}; +module.exports = new CloudCmdProto(DOM); load.addErrorListener((e, src) => { const msg = `file ${src} could not be loaded`; Images.show.error(msg); }); -function CloudCmdProto({DOM, Listeners}) { +function CloudCmdProto(DOM) { + let Listeners; + Emitify.call(this); const CloudCmd = this; @@ -46,9 +49,11 @@ function CloudCmdProto({DOM, Listeners}) { const {Storage, Files} = DOM; - this.log = () => { + this.log = (...a) => { if (!isDev) return; + + console.log(...a); }; this.prefix = ''; this.prefixSocket = ''; @@ -225,6 +230,7 @@ function CloudCmdProto({DOM, Listeners}) { const dirPath = DOM.getCurrentDirPath(); + ({Listeners} = CloudCmd); Listeners.init(); const panels = getPanels(); diff --git a/client/cloudcmd.mjs b/client/cloudcmd.js similarity index 50% rename from client/cloudcmd.mjs rename to client/cloudcmd.js index 0dac1a03..d4bac384 100644 --- a/client/cloudcmd.mjs +++ b/client/cloudcmd.js @@ -1,44 +1,39 @@ -import process from 'node:process'; -import wraptile from 'wraptile'; -import load from 'load.js'; -import '../css/main.css'; -import {registerSW, listenSW} from './sw/register.js'; -import {initSortPanel, sortPanel} from './sort.mjs'; -import Util from '../common/util.js'; -import * as CloudFunc from '../common/cloudfunc.mjs'; -import DOM from './dom/index.js'; -import {createCloudCmd} from './client.mjs'; -import * as Listeners from './listeners/index.js'; +'use strict'; + +const process = require('node:process'); +require('../css/main.css'); + +const wraptile = require('wraptile'); +const load = require('load.js'); + +const {registerSW, listenSW} = require('./sw/register'); const isDev = process.env.NODE_ENV === 'development'; -export default init; - -globalThis.CloudCmd = init; - -async function init(config) { - globalThis.CloudCmd = createCloudCmd({ - DOM, - Listeners, - }); - globalThis.DOM = DOM; - globalThis.Util = Util; - globalThis.CloudFunc = CloudFunc; +module.exports = async (config) => { + window.Util = require('../common/util'); + window.CloudFunc = require('../common/cloudfunc'); + + window.DOM = require('./dom'); + window.CloudCmd = require('./client'); await register(config); - initSortPanel(); - globalThis.CloudCmd.sortPanel = sortPanel; + require('./listeners'); + require('./key'); + require('./sort'); + const prefix = getPrefix(config.prefix); - globalThis.CloudCmd.init(prefix, config); + window.CloudCmd.init(prefix, config); - if (globalThis.CloudCmd.config('menu') === 'aleman') + if (window.CloudCmd.config('menu') === 'aleman') setTimeout(() => { import('https://esm.sh/@putout/processor-html'); import('https://esm.sh/@putout/bundle'); }, 100); -} +}; +window.CloudCmd = module.exports; function getPrefix(prefix) { if (!prefix) @@ -54,7 +49,7 @@ const onUpdateFound = wraptile(async (config) => { if (isDev) return; - const {DOM} = globalThis; + const {DOM} = window; const prefix = getPrefix(config.prefix); await load.js(`${prefix}/dist/cloudcmd.common.js`); @@ -63,7 +58,7 @@ const onUpdateFound = wraptile(async (config) => { console.log('cloudcmd: sw: updated'); DOM.Events.removeAll(); - globalThis.CloudCmd(config); + window.CloudCmd(config); }); async function register(config) { diff --git a/client/dom/buffer.js b/client/dom/buffer.js new file mode 100644 index 00000000..afa78dd7 --- /dev/null +++ b/client/dom/buffer.js @@ -0,0 +1,135 @@ +'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 deleted file mode 100644 index 01e8142a..00000000 --- a/client/dom/buffer.mjs +++ /dev/null @@ -1,124 +0,0 @@ -/* 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.mjs b/client/dom/current-file.js similarity index 83% rename from client/dom/current-file.mjs rename to client/dom/current-file.js index e55acbe9..1150de2a 100644 --- a/client/dom/current-file.mjs +++ b/client/dom/current-file.js @@ -1,8 +1,13 @@ +'use strict'; + /* global DOM */ /* global CloudCmd */ -import createElement from '@cloudcmd/create-element'; -import {encode, decode} from '../../common/entity.js'; -import {getTitle, FS} from '../../common/cloudfunc.mjs'; +const createElement = require('@cloudcmd/create-element'); +const {atob, btoa} = require('../../common/base64'); + +const {encode, decode} = require('../../common/entity'); + +const {getTitle, FS} = require('../../common/cloudfunc'); let Title; @@ -10,15 +15,14 @@ const CURRENT_FILE = 'current-file'; const encodeNBSP = (a) => a?.replace('\xa0', ' '); const decodeNBSP = (a) => a?.replace(' ', '\xa0'); -export const _CURRENT_FILE = CURRENT_FILE; - +module.exports._CURRENT_FILE = CURRENT_FILE; /** * set name from current (or param) file * * @param name * @param current */ -export const setCurrentName = (name, current) => { +module.exports.setCurrentName = (name, current) => { const Info = DOM.CurrentInfo; const {link} = Info; const {prefix} = CloudCmd; @@ -40,7 +44,7 @@ export const setCurrentName = (name, current) => { * * @param currentFile */ -export const getCurrentName = (currentFile) => { +module.exports.getCurrentName = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); if (!current) @@ -67,19 +71,18 @@ const parseNameAttribute = (attribute) => { return decodeNBSP(decodeURI(atob(attribute))); }; -export const _parseNameAttribute = parseNameAttribute; +module.exports._parseNameAttribute = parseNameAttribute; const parseHrefAttribute = (prefix, attribute) => { attribute = attribute.replace(RegExp('^' + prefix + FS), ''); return decode(decodeNBSP(attribute)); }; -export const _parseHrefAttribute = parseHrefAttribute; - +module.exports._parseHrefAttribute = parseHrefAttribute; /** * get current direcotory path */ -export const getCurrentDirPath = (panel = DOM.getPanel()) => { +module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => { const path = DOM.getByDataName('js-path', panel); return path.textContent; }; @@ -89,7 +92,7 @@ export const getCurrentDirPath = (panel = DOM.getPanel()) => { * * @param currentFile - current file by default */ -export const getCurrentPath = (currentFile) => { +module.exports.getCurrentPath = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const [element] = DOM.getByTag('a', current); const {prefix} = CloudCmd; @@ -100,7 +103,7 @@ export const getCurrentPath = (currentFile) => { /** * get current direcotory name */ -export const getCurrentDirName = () => { +module.exports.getCurrentDirName = () => { const href = DOM .getCurrentDirPath() .replace(/\/$/, ''); @@ -113,7 +116,7 @@ export const getCurrentDirName = () => { /** * get current direcotory path */ -export const getParentDirPath = (panel) => { +module.exports.getParentDirPath = (panel) => { const path = DOM.getCurrentDirPath(panel); const dirName = DOM.getCurrentDirName() + '/'; const index = path.lastIndexOf(dirName); @@ -127,7 +130,7 @@ export const getParentDirPath = (panel) => { /** * get not current direcotory path */ -export const getNotCurrentDirPath = () => { +module.exports.getNotCurrentDirPath = () => { const panel = DOM.getPanel({ active: false, }); @@ -140,14 +143,14 @@ export const getNotCurrentDirPath = () => { * * @currentFile */ -export const getCurrentFile = () => { +module.exports.getCurrentFile = () => { return DOM.getByClass(CURRENT_FILE); }; /** * get current file by name */ -export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => { +module.exports.getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => { const dataName = 'js-file-' + btoa(encodeURI(encodeNBSP(name))); return DOM.getByDataName(dataName, panel); }; @@ -169,7 +172,7 @@ function unsetCurrentFile(currentFile) { /** * unified way to set current file */ -export const setCurrentFile = (currentFile, options) => { +module.exports.setCurrentFile = (currentFile, options) => { const o = options; const currentFileWas = DOM.getCurrentFile(); @@ -216,7 +219,7 @@ export const setCurrentFile = (currentFile, options) => { return DOM; }; -export const setCurrentByName = (name) => { +this.setCurrentByName = (name) => { const current = DOM.getCurrentByName(name); return DOM.setCurrentFile(current); }; @@ -227,7 +230,7 @@ export const setCurrentByName = (name) => { * @param layer - element * @param - position {x, y} */ -export const getCurrentByPosition = ({x, y}) => { +module.exports.getCurrentByPosition = ({x, y}) => { const element = document.elementFromPoint(x, y); const getEl = (el) => { @@ -259,7 +262,7 @@ export const getCurrentByPosition = ({x, y}) => { * * @param currentFile */ -export const isCurrentFile = (currentFile) => { +module.exports.isCurrentFile = (currentFile) => { if (!currentFile) return false; @@ -271,7 +274,7 @@ export const isCurrentFile = (currentFile) => { * * @param name */ -export const setTitle = (name) => { +module.exports.setTitle = (name) => { if (!Title) Title = DOM.getByTag('title')[0] || createElement('title', { innerHTML: name, @@ -288,18 +291,18 @@ export const setTitle = (name) => { * * @param currentFile */ -export const isCurrentIsDir = (currentFile) => { +module.exports.isCurrentIsDir = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const path = DOM.getCurrentPath(current); const fileType = DOM.getCurrentType(current); - const isZip = path.endsWith('.zip'); + const isZip = /\.zip$/.test(path); const isDir = /^directory(-link)?/.test(fileType); return isDir || isZip; }; -export const getCurrentType = (currentFile) => { +module.exports.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.mjs b/client/dom/current-file.spec.js similarity index 68% rename from client/dom/current-file.spec.mjs rename to client/dom/current-file.spec.js index 2a576dce..de1ba2af 100644 --- a/client/dom/current-file.spec.mjs +++ b/client/dom/current-file.spec.js @@ -1,18 +1,20 @@ -import {test, stub} from 'supertape'; -import {create} from 'auto-globals'; -import wraptile from 'wraptile'; -import * as currentFile from './current-file.mjs'; +'use strict'; +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); const {_CURRENT_FILE} = currentFile; test('current-file: setCurrentName: setAttribute', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM(); - globalThis.CloudCmd = getCloudCmd(); + global.DOM = getDOM(); + global.CloudCmd = getCloudCmd(); const current = create(); const {setAttribute} = current; @@ -21,17 +23,17 @@ test('current-file: setCurrentName: setAttribute', (t) => { t.calledWith(setAttribute, ['data-name', 'js-file-aGVsbG8='], 'should call setAttribute'); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.end(); }); test('current-file: setCurrentName: setAttribute: cyrillic', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM(); - globalThis.CloudCmd = getCloudCmd(); + global.DOM = getDOM(); + global.CloudCmd = getCloudCmd(); const current = create(); const {setAttribute} = current; @@ -40,8 +42,8 @@ test('current-file: setCurrentName: setAttribute: cyrillic', (t) => { t.calledWith(setAttribute, ['data-name', 'js-file-JUQwJUIwJUQwJUI5'], 'should call setAttribute'); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.end(); }); @@ -57,12 +59,12 @@ test('current-file: getCurrentName', (t) => { }); test('current-file: emit', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; const emit = stub(); - globalThis.DOM = getDOM(); - globalThis.CloudCmd = getCloudCmd({ + global.DOM = getDOM(); + global.CloudCmd = getCloudCmd({ emit, }); @@ -72,22 +74,22 @@ test('current-file: emit', (t) => { t.calledWith(emit, ['current-file', current], 'should call emit'); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.end(); }); test('current-file: setCurrentName: return', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; const link = {}; - globalThis.DOM = getDOM({ + global.DOM = getDOM({ link, }); - globalThis.CloudCmd = getCloudCmd(); + global.CloudCmd = getCloudCmd(); const current = create(); @@ -95,19 +97,19 @@ test('current-file: setCurrentName: return', (t) => { t.equal(result, link, 'should return link'); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.end(); }); test('current-file: getParentDirPath: result', (t) => { - const {DOM} = globalThis; + const {DOM} = global; const getCurrentDirPath = returns('/D/Films/+++favorite films/'); const getCurrentDirName = returns('+++favorite films'); - globalThis.DOM = getDOM({ + global.DOM = getDOM({ getCurrentDirPath, getCurrentDirName, }); @@ -115,55 +117,55 @@ test('current-file: getParentDirPath: result', (t) => { const result = currentFile.getParentDirPath(); const expected = '/D/Films/'; - globalThis.DOM = DOM; + global.DOM = DOM; t.equal(result, expected, 'should return parent dir path'); t.end(); }); test('current-file: isCurrentFile: no', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM(); - globalThis.CloudCmd = getCloudCmd(); + global.DOM = getDOM(); + global.CloudCmd = getCloudCmd(); const result = currentFile.isCurrentFile(); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.notOk(result); t.end(); }); test('current-file: isCurrentFile', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; const isContainClass = stub(); - globalThis.DOM = getDOM({ + global.DOM = getDOM({ isContainClass, }); - globalThis.CloudCmd = getCloudCmd(); + global.CloudCmd = getCloudCmd(); const current = {}; currentFile.isCurrentFile(current); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass'); t.end(); }); test('current-file: getCurrentType', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM(); - globalThis.CloudCmd = getCloudCmd(); + global.DOM = getDOM(); + global.CloudCmd = getCloudCmd(); - const {getByDataName} = globalThis.DOM; + const {getByDataName} = global.DOM; getByDataName.returns({ className: 'mini-icon directory', @@ -173,87 +175,87 @@ test('current-file: getCurrentType', (t) => { currentFile.getCurrentType(current); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.calledWith(getByDataName, ['js-type', current]); t.end(); }); test('current-file: isCurrentIsDir: getCurrentType', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM(); - globalThis.CloudCmd = getCloudCmd(); + global.DOM = getDOM(); + global.CloudCmd = getCloudCmd(); - const {getCurrentType} = globalThis.DOM; + const {getCurrentType} = global.DOM; const current = create(); currentFile.isCurrentIsDir(current); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.calledWith(getCurrentType, [current]); t.end(); }); test('current-file: isCurrentIsDir: directory', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM({ + global.DOM = getDOM({ getCurrentType: stub().returns('directory'), }); - globalThis.CloudCmd = getCloudCmd(); + global.CloudCmd = getCloudCmd(); const current = create(); const result = currentFile.isCurrentIsDir(current); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.ok(result); t.end(); }); test('current-file: isCurrentIsDir: directory-link', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM({ + global.DOM = getDOM({ getCurrentType: stub().returns('directory-link'), }); - globalThis.CloudCmd = getCloudCmd(); + global.CloudCmd = getCloudCmd(); const current = create(); const result = currentFile.isCurrentIsDir(current); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.ok(result); t.end(); }); test('current-file: isCurrentIsDir: file', (t) => { - const {DOM, CloudCmd} = globalThis; + const {DOM, CloudCmd} = global; - globalThis.DOM = getDOM({ + global.DOM = getDOM({ getCurrentType: stub().returns('file'), }); - globalThis.CloudCmd = getCloudCmd(); + global.CloudCmd = getCloudCmd(); const current = create(); const result = currentFile.isCurrentIsDir(current); - globalThis.DOM = DOM; - globalThis.CloudCmd = CloudCmd; + global.DOM = DOM; + global.CloudCmd = CloudCmd; t.notOk(result); t.end(); @@ -289,7 +291,7 @@ function getDOM(overrides = {}) { getByDataName = stub(), isContainClass = stub(), getCurrentType = stub(), - getCurrentPath = stub().returns(''), + getCurrentPath = stub(), } = overrides; return { diff --git a/client/dom/dialog.js b/client/dom/dialog.js index eb342221..f4751456 100644 --- a/client/dom/dialog.js +++ b/client/dom/dialog.js @@ -1,6 +1,6 @@ 'use strict'; -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const { alert, diff --git a/client/dom/directory.js b/client/dom/directory.js index bedb7e95..c7cfa386 100644 --- a/client/dom/directory.js +++ b/client/dom/directory.js @@ -3,8 +3,8 @@ /* global CloudCmd */ const philip = require('philip'); -const Images = require('./images.mjs'); -const {FS} = require('../../common/cloudfunc.mjs'); +const Images = require('./images'); +const {FS} = require('../../common/cloudfunc'); const DOM = require('.'); const Dialog = require('./dialog'); diff --git a/client/dom/dom-tree.spec.js b/client/dom/dom-tree.spec.js index 606df179..044e97b0 100644 --- a/client/dom/dom-tree.spec.js +++ b/client/dom/dom-tree.spec.js @@ -2,7 +2,7 @@ const test = require('supertape'); const {create} = require('auto-globals'); -const {tryCatch} = require('try-catch'); +const tryCatch = require('try-catch'); const {isContainClass} = require('./dom-tree'); diff --git a/client/dom/events/index.js b/client/dom/events/index.js new file mode 100644 index 00000000..22e9261e --- /dev/null +++ b/client/dom/events/index.js @@ -0,0 +1,198 @@ +'use strict'; + +const itype = require('itype'); +const EventStore = require('./event-store'); + +module.exports = new EventsProto(); + +function EventsProto() { + const Events = this; + + const getEventOptions = (eventName) => { + if (eventName !== 'touchstart') + return false; + + return { + passive: true, + }; + }; + + function parseArgs(eventName, element, listener, callback) { + let isFunc; + const args = [ + eventName, + element, + listener, + callback, + ]; + + const EVENT_NAME = 1; + const ELEMENT = 0; + const type = itype(eventName); + + switch(type) { + default: + if (!type.endsWith('element')) + throw Error(`unknown eventName: ${type}`); + + parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback); + break; + + case 'string': + isFunc = itype.function(element); + + if (isFunc) { + listener = element; + element = null; + } + + if (!element) + element = window; + + callback(element, [ + eventName, + listener, + getEventOptions(eventName), + ]); + break; + + case 'array': + + for (const name of eventName) { + parseArgs(name, element, listener, callback); + } + + break; + + case 'object': + + for (const name of Object.keys(eventName)) { + const eventListener = eventName[name]; + + parseArgs(name, element, eventListener, callback); + } + + break; + } + } + + /** + * safe add event listener + * + * @param type + * @param element - document by default + * @param listener + */ + this.add = (type, element, listener) => { + checkType(type); + + parseArgs(type, element, listener, (element, args) => { + const [name, fn, options] = args; + + element.addEventListener(name, fn, options); + EventStore.add(element, name, fn); + }); + + return Events; + }; + + /** + * safe add event listener + * + * @param type + * @param listener + * @param element - document by default + */ + this.addOnce = (type, element, listener) => { + const once = (event) => { + Events.remove(type, element, once); + listener(event); + }; + + if (!listener) { + listener = element; + element = null; + } + + this.add(type, element, once); + + return Events; + }; + + /** + * safe remove event listener + * + * @param type + * @param listener + * @param element - document by default + */ + this.remove = (type, element, listener) => { + checkType(type); + + parseArgs(type, element, listener, (element, args) => { + element.removeEventListener(...args); + }); + + return Events; + }; + + /** + * remove all added event listeners + */ + this.removeAll = () => { + const events = EventStore.get(); + + for (const [el, name, fn] of events) + el.removeEventListener(name, fn); + + EventStore.clear(); + }; + + /** + * safe add event keydown listener + * + * @param args + */ + this.addKey = function(...args) { + return Events.add('keydown', ...args); + }; + + /** + * safe remove event click listener + * + * @param args + */ + this.rmKey = function(...args) { + return Events.remove('keydown', ...args); + }; + + /** + * safe add event click listener + */ + this.addClick = function(...args) { + return Events.add('click', ...args); + }; + + /** + * safe remove event click listener + */ + this.rmClick = function(...args) { + return Events.remove('click', ...args); + }; + + this.addContextMenu = function(...args) { + return Events.add('contextmenu', ...args); + }; + + /** + * safe add load listener + */ + this.addLoad = function(...args) { + return Events.add('load', ...args); + }; + + function checkType(type) { + if (!type) + throw Error('type could not be empty!'); + } +} diff --git a/client/dom/events/index.mjs b/client/dom/events/index.mjs deleted file mode 100644 index 0762d560..00000000 --- a/client/dom/events/index.mjs +++ /dev/null @@ -1,203 +0,0 @@ -import itype from 'itype'; -import EventStore from './event-store.js'; - -/** - * safe add event listener - * - * @param type - * @param element - document by default - * @param listener - */ -export const add = (type, element, listener) => { - checkType(type); - - parseArgs(type, element, listener, (element, args) => { - const [name, fn, options] = args; - - element.addEventListener(name, fn, options); - EventStore.add(element, name, fn); - }); - - return Events; -}; - -/** - * safe add event listener - * - * @param type - * @param listener - * @param element - document by default - */ -export const addOnce = (type, element, listener) => { - const once = (event) => { - Events.remove(type, element, once); - listener(event); - }; - - if (!listener) { - listener = element; - element = null; - } - - add(type, element, once); - - return Events; -}; - -/** - * safe remove event listener - * - * @param type - * @param listener - * @param element - document by default - */ -export const remove = (type, element, listener) => { - checkType(type); - - parseArgs(type, element, listener, (element, args) => { - element.removeEventListener(...args); - }); - - return Events; -}; - -/** - * remove all added event listeners - */ -export const removeAll = () => { - const events = EventStore.get(); - - for (const [el, name, fn] of events) - el.removeEventListener(name, fn); - - EventStore.clear(); -}; - -/** - * safe add event keydown listener - * - * @param args - */ -export const addKey = function(...args) { - return add('keydown', ...args); -}; - -/** - * safe remove event click listener - * - * @param args - */ -export const rmKey = function(...args) { - return Events.remove('keydown', ...args); -}; - -/** - * safe add event click listener - */ -export const addClick = function(...args) { - return Events.add('click', ...args); -}; - -/** - * safe remove event click listener - */ -export const rmClick = function(...args) { - return remove('click', ...args); -}; - -export const addContextMenu = function(...args) { - return add('contextmenu', ...args); -}; - -/** - * safe add load listener - */ -export const addLoad = function(...args) { - return add('load', ...args); -}; - -function checkType(type) { - if (!type) - throw Error('type could not be empty!'); -} - -const getEventOptions = (eventName) => { - if (eventName !== 'touchstart') - return false; - - return { - passive: true, - }; -}; - -function parseArgs(eventName, element, listener, callback) { - let isFunc; - const args = [ - eventName, - element, - listener, - callback, - ]; - - const EVENT_NAME = 1; - const ELEMENT = 0; - const type = itype(eventName); - - switch(type) { - default: - if (!type.endsWith('element')) - throw Error(`unknown eventName: ${type}`); - - parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback); - break; - - case 'string': - isFunc = itype.function(element); - - if (isFunc) { - listener = element; - element = null; - } - - if (!element) - element = window; - - callback(element, [ - eventName, - listener, - getEventOptions(eventName), - ]); - break; - - case 'array': - - for (const name of eventName) { - parseArgs(name, element, listener, callback); - } - - break; - - case 'object': - - for (const name of Object.keys(eventName)) { - const eventListener = eventName[name]; - - parseArgs(name, element, eventListener, callback); - } - - break; - } -} - -const Events = { - add, - addClick, - addContextMenu, - addKey, - addLoad, - addOnce, - remove, - removeAll, - rmClick, - rmKey, -}; diff --git a/client/dom/images.mjs b/client/dom/images.js similarity index 84% rename from client/dom/images.mjs rename to client/dom/images.js index 9682e666..bb5579c1 100644 --- a/client/dom/images.mjs +++ b/client/dom/images.js @@ -1,5 +1,10 @@ /* global DOM */ -import createElement from '@cloudcmd/create-element'; + +'use strict'; + +const createElement = require('@cloudcmd/create-element'); + +const Images = module.exports; const LOADING = 'loading'; const HIDDEN = 'hidden'; @@ -7,8 +12,7 @@ const ERROR = 'error'; const getLoadingType = () => isSVG() ? '-svg' : '-gif'; -export const get = getElement; - +module.exports.get = getElement; /** * check SVG SMIL animation support */ @@ -36,7 +40,7 @@ function getElement() { } /* Функция создаёт картинку загрузки */ -export const loading = () => { +module.exports.loading = () => { const element = getElement(); const {classList} = element; const loadingImage = LOADING + getLoadingType(); @@ -48,7 +52,7 @@ export const loading = () => { }; /* Функция создаёт картинку ошибки загрузки */ -export const error = () => { +module.exports.error = () => { const element = getElement(); const {classList} = element; const loadingImage = LOADING + getLoadingType(); @@ -59,21 +63,14 @@ export const error = () => { return element; }; -show.load = show; -show.error = (text) => { - const image = Images.error(); - - DOM.show(image); - image.title = text; - - return image; -}; - +module.exports.show = show; +module.exports.show.load = show; +module.exports.show.error = error; /** * Function shows loading spinner * position = {top: true}; */ -export function show(position, panel) { +function show(position, panel) { const image = Images.loading(); const parent = image.parentElement; const refreshButton = DOM.getRefreshButton(panel); @@ -99,10 +96,19 @@ export function show(position, panel) { return image; } +function error(text) { + const image = Images.error(); + + DOM.show(image); + image.title = text; + + return image; +} + /** * hide load image */ -export const hide = () => { +module.exports.hide = () => { const element = Images.get(); DOM.hide(element); @@ -110,7 +116,7 @@ export const hide = () => { return Images; }; -export const setProgress = (value, title) => { +module.exports.setProgress = (value, title) => { const DATA = 'data-progress'; const element = Images.get(); @@ -125,7 +131,7 @@ export const setProgress = (value, title) => { return Images; }; -export const clearProgress = () => { +module.exports.clearProgress = () => { const DATA = 'data-progress'; const element = Images.get(); @@ -137,13 +143,3 @@ export const 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 27a1a5c6..bfb4386c 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.mjs'); +const Images = require('./images'); const RESTful = require('./rest'); const Storage = require('./storage'); const renameCurrent = require('./operations/rename-current'); -const CurrentFile = require('./current-file.mjs'); +const CurrentFile = require('./current-file'); const DOMTree = require('./dom-tree'); const Cmd = module.exports; @@ -32,8 +32,8 @@ DOM.CurrentInfo = CurrentInfo; module.exports = DOM; DOM.uploadDirectory = require('./directory'); -DOM.Buffer = require('./buffer.mjs'); -DOM.Events = require('#dom/events'); +DOM.Buffer = require('./buffer'); +DOM.Events = require('./events'); const loadRemote = require('./load-remote'); const selectByPattern = require('./select-by-pattern'); @@ -416,7 +416,7 @@ module.exports.shrinkSelection = () => { * setting history wrapper */ module.exports.setHistory = (data, title, url) => { - const ret = globalThis.history; + const ret = window.history; const {prefix} = CloudCmd; url = prefix + url; @@ -554,7 +554,7 @@ module.exports.getPanel = (options) => { * then always work with passive * panel */ - if (globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH) + if (window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH) panel = DOM.getByDataName('js-left'); if (!panel) @@ -729,19 +729,17 @@ module.exports.getPackerExt = (type) => { return '.tar.gz'; }; -module.exports.goToDirectory = async (overrides = {}) => { - const {Dialog} = DOM; - const {prompt = Dialog.prompt, changeDir = CloudCmd.changeDir} = overrides; - +module.exports.goToDirectory = async () => { const msg = 'Go to directory:'; + const {Dialog} = DOM; const {dirPath} = CurrentInfo; - const [cancel, path = dirPath] = await prompt(msg, dirPath); + const [cancel, path = dirPath] = await Dialog.prompt(msg, dirPath); if (cancel) return; - await changeDir(path); + await CloudCmd.changeDir(path); }; module.exports.duplicatePanel = async () => { diff --git a/client/dom/index.spec.js b/client/dom/index.spec.js index e3a4b4bc..c5843d11 100644 --- a/client/dom/index.spec.js +++ b/client/dom/index.spec.js @@ -3,20 +3,30 @@ require('css-modules-require-hook/preset'); const {test, stub} = require('supertape'); -const {getCSSVar, goToDirectory} = require('./index'); +const mockRequire = require('mock-require'); +const {getCSSVar} = require('./index'); +const {reRequire, stopAll} = mockRequire; -globalThis.CloudCmd = {}; +global.CloudCmd = {}; test('cloudcmd: client: dom: goToDirectory', async (t) => { const path = ''; + const {CloudCmd} = global; const changeDir = stub(); const prompt = stub().returns([null, path]); - await goToDirectory({ + CloudCmd.changeDir = changeDir; + + mockRequire('./dialog', { prompt, - changeDir, }); + const {goToDirectory} = reRequire('.'); + + await goToDirectory(); + + stopAll(); + t.calledWith(changeDir, [path]); t.end(); }); @@ -25,14 +35,14 @@ test('cloudcmd: client: dom: getCSSVar', (t) => { const body = {}; const getPropertyValue = stub().returns(0); - globalThis.getComputedStyle = stub().returns({ + global.getComputedStyle = stub().returns({ getPropertyValue, }); const result = getCSSVar('hello', { body, }); - delete globalThis.getComputedStyle; + delete global.getComputedStyle; t.notOk(result); t.end(); @@ -42,14 +52,14 @@ test('cloudcmd: client: dom: getCSSVar: 1', (t) => { const body = {}; const getPropertyValue = stub().returns(1); - globalThis.getComputedStyle = stub().returns({ + global.getComputedStyle = stub().returns({ getPropertyValue, }); const result = getCSSVar('hello', { body, }); - delete globalThis.getComputedStyle; + delete global.getComputedStyle; t.ok(result); t.end(); diff --git a/client/dom/io/index.js b/client/dom/io/index.js index a17360ae..577c357a 100644 --- a/client/dom/io/index.js +++ b/client/dom/io/index.js @@ -1,14 +1,14 @@ 'use strict'; -const {FS} = require('../../../common/cloudfunc.mjs'); -const _sendRequest = require('./send-request'); +const {FS} = require('../../../common/cloudfunc'); +const sendRequest = require('./send-request'); const imgPosition = { top: true, }; module.exports.delete = async (url, data) => { - return await _sendRequest({ + return await sendRequest({ method: 'DELETE', url: FS + url, data, @@ -19,7 +19,7 @@ module.exports.delete = async (url, data) => { }; module.exports.patch = async (url, data) => { - return await _sendRequest({ + return await sendRequest({ method: 'PATCH', url: FS + url, data, @@ -28,7 +28,7 @@ module.exports.patch = async (url, data) => { }; module.exports.write = async (url, data) => { - return await _sendRequest({ + return await sendRequest({ method: 'PUT', url: FS + url, data, @@ -36,11 +36,7 @@ module.exports.write = async (url, data) => { }); }; -module.exports.createDirectory = async (url, overrides = {}) => { - const { - sendRequest = _sendRequest, - } = overrides; - +module.exports.createDirectory = async (url) => { return await sendRequest({ method: 'PUT', url: `${FS}${url}?dir`, @@ -51,7 +47,7 @@ module.exports.createDirectory = async (url, overrides = {}) => { module.exports.read = async (url, dataType = 'text') => { const notLog = !url.includes('?'); - return await _sendRequest({ + return await sendRequest({ method: 'GET', url: FS + url, notLog, @@ -60,7 +56,7 @@ module.exports.read = async (url, dataType = 'text') => { }; module.exports.copy = async (from, to, names) => { - return await _sendRequest({ + return await sendRequest({ method: 'PUT', url: '/copy', data: { @@ -73,7 +69,7 @@ module.exports.copy = async (from, to, names) => { }; module.exports.pack = async (data) => { - return await _sendRequest({ + return await sendRequest({ method: 'PUT', url: '/pack', data, @@ -81,7 +77,7 @@ module.exports.pack = async (data) => { }; module.exports.extract = async (data) => { - return await _sendRequest({ + return await sendRequest({ method: 'PUT', url: '/extract', data, @@ -89,7 +85,7 @@ module.exports.extract = async (data) => { }; module.exports.move = async (from, to, names) => { - return await _sendRequest({ + return await sendRequest({ method: 'PUT', url: '/move', data: { @@ -102,7 +98,7 @@ module.exports.move = async (from, to, names) => { }; module.exports.rename = async (from, to) => { - return await _sendRequest({ + return await sendRequest({ method: 'PUT', url: '/rename', data: { @@ -115,7 +111,7 @@ module.exports.rename = async (from, to) => { module.exports.Config = { read: async () => { - return await _sendRequest({ + return await sendRequest({ method: 'GET', url: '/config', imgPosition, @@ -124,7 +120,7 @@ module.exports.Config = { }, write: async (data) => { - return await _sendRequest({ + return await sendRequest({ method: 'PATCH', url: '/config', data, @@ -135,7 +131,7 @@ module.exports.Config = { module.exports.Markdown = { read: async (url) => { - return await _sendRequest({ + return await sendRequest({ method: 'GET', url: `/markdown${url}`, imgPosition, @@ -144,7 +140,7 @@ module.exports.Markdown = { }, render: async (data) => { - return await _sendRequest({ + return await sendRequest({ method: 'PUT', url: '/markdown', data, diff --git a/client/dom/io/index.spec.js b/client/dom/io/index.spec.js index 19ebe5bd..2212f91a 100644 --- a/client/dom/io/index.spec.js +++ b/client/dom/io/index.spec.js @@ -1,14 +1,18 @@ 'use strict'; const {test, stub} = require('supertape'); -const io = require('.'); + +const mockRequire = require('mock-require'); + +const {reRequire, stopAll} = mockRequire; test('client: dom: io', (t) => { const sendRequest = stub(); + mockRequire('./send-request', sendRequest); - io.createDirectory('/hello', { - sendRequest, - }); + const io = reRequire('.'); + + io.createDirectory('/hello'); const expected = { imgPosition: { @@ -18,6 +22,8 @@ test('client: dom: io', (t) => { url: '/fs/hello?dir', }; + stopAll(); + t.calledWith(sendRequest, [expected]); t.end(); }); diff --git a/client/dom/io/send-request.js b/client/dom/io/send-request.js index c61544f1..bc52d667 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.mjs'); +const Images = require('../images'); const load = require('../load'); module.exports = promisify((params, callback) => { diff --git a/client/dom/load-remote.js b/client/dom/load-remote.js index b1b798a8..84c7e329 100644 --- a/client/dom/load-remote.js +++ b/client/dom/load-remote.js @@ -4,7 +4,7 @@ const rendy = require('rendy'); const itype = require('itype'); const load = require('load.js'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const {findObjByNameInArr} = require('../../common/util'); diff --git a/client/dom/load.js b/client/dom/load.js index d060a92c..03c25d73 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.mjs'); +const Images = require('./images'); module.exports.getIdBySrc = getIdBySrc; /** diff --git a/client/dom/operations/rename-current.js b/client/dom/operations/rename-current.js index ab658edf..5b2e0ff7 100644 --- a/client/dom/operations/rename-current.js +++ b/client/dom/operations/rename-current.js @@ -3,29 +3,21 @@ /* global CloudCmd */ const capitalize = require('just-capitalize'); -const _Dialog = require('../dialog'); +const Dialog = require('../dialog'); const Storage = require('../storage'); const RESTful = require('../rest'); -const _currentFile = require('../current-file.mjs'); +const { + isCurrentFile, + getCurrentName, + getCurrentFile, + getCurrentByName, + getCurrentType, + getCurrentDirPath, + setCurrentName, +} = require('../current-file'); -module.exports = async (current, overrides = {}) => { - const { - refresh = CloudCmd.refresh, - Dialog = _Dialog, - currentFile = _currentFile, - } = overrides; - - const { - isCurrentFile, - getCurrentName, - getCurrentFile, - getCurrentByName, - getCurrentType, - getCurrentDirPath, - setCurrentName, - } = currentFile; - +module.exports = async (current) => { if (!isCurrentFile(current)) current = getCurrentFile(); @@ -66,5 +58,5 @@ module.exports = async (current, overrides = {}) => { setCurrentName(to, current); Storage.remove(dirPath); - refresh(); + CloudCmd.refresh(); }; diff --git a/client/dom/operations/rename-current.spec.js b/client/dom/operations/rename-current.spec.js index 4a3c7fec..f39ff6bf 100644 --- a/client/dom/operations/rename-current.spec.js +++ b/client/dom/operations/rename-current.spec.js @@ -2,20 +2,23 @@ const {test, stub} = require('supertape'); -const renameCurrent = require('./rename-current'); +const mockRequire = require('mock-require'); + +const {reRequire, stopAll} = mockRequire; test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => { const current = {}; const isCurrentFile = stub(); - const currentFile = stubCurrentFile({ + mockRequire('../dialog', stubDialog()); + mockRequire('../current-file', stubCurrentFile({ isCurrentFile, - }); + })); - await renameCurrent(current, { - Dialog: stubDialog(), - currentFile, - }); + const renameCurrent = reRequire('./rename-current'); + await renameCurrent(current); + + stopAll(); t.calledWith(isCurrentFile, [current], 'should call isCurrentFile'); t.end(); @@ -24,6 +27,11 @@ test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => { test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => { const current = {}; const name = 'hello'; + const {CloudCmd} = global; + + global.CloudCmd = { + refresh: stub(), + }; const prompt = stub().returns([null, name]); const confirm = stub().returns([true]); @@ -31,23 +39,25 @@ test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => { const getCurrentByName = stub().returns(current); const getCurrentType = stub().returns('directory'); - const Dialog = stubDialog({ + mockRequire('../dialog', stubDialog({ confirm, prompt, - }); + })); - const currentFile = stubCurrentFile({ + mockRequire('../current-file', stubCurrentFile({ getCurrentByName, getCurrentType, - }); + })); - await renameCurrent(null, { - Dialog, - currentFile, - }); + const renameCurrent = reRequire('./rename-current'); + await renameCurrent(); const expected = 'Directory "hello" already exists. Proceed?'; + global.CloudCmd = CloudCmd; + + stopAll(); + t.calledWith(confirm, [expected], 'should call confirm'); t.end(); }); diff --git a/client/dom/rest.js b/client/dom/rest.js index 7596f620..9c638446 100644 --- a/client/dom/rest.js +++ b/client/dom/rest.js @@ -1,10 +1,10 @@ 'use strict'; -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const {encode} = require('../../common/entity'); -const Images = require('./images.mjs'); +const Images = require('./images'); const IO = require('./io'); const Dialog = require('./dialog'); diff --git a/client/dom/storage.spec.js b/client/dom/storage.spec.js index 0b281ffc..abd7258a 100644 --- a/client/dom/storage.spec.js +++ b/client/dom/storage.spec.js @@ -7,58 +7,58 @@ const storage = require('./storage'); const {stringify} = JSON; test('cloudcmd: client: storage: set', async (t) => { - const {localStorage} = globalThis; + const {localStorage} = global; const setItem = stub(); - globalThis.localStorage = { + global.localStorage = { setItem, }; await storage.set('hello', 'world'); - globalThis.localStorage = localStorage; + global.localStorage = localStorage; t.calledWith(setItem, ['hello', 'world'], 'should call setItem'); t.end(); }); test('cloudcmd: client: storage: get', async (t) => { - const {localStorage} = globalThis; + const {localStorage} = global; const getItem = stub().returns('world'); - globalThis.localStorage = { + global.localStorage = { getItem, }; const result = await storage.get('hello'); - globalThis.localStorage = localStorage; + global.localStorage = localStorage; t.equal(result, 'world'); t.end(); }); test('cloudcmd: client: storage: getJson', async (t) => { - const {localStorage} = globalThis; + const {localStorage} = global; const expected = { hello: 'world', }; const getItem = stub().returns(stringify(expected)); - globalThis.localStorage = { + global.localStorage = { getItem, }; const result = await storage.getJson('hello'); - globalThis.localStorage = localStorage; + global.localStorage = localStorage; t.deepEqual(result, expected); t.end(); }); test('cloudcmd: client: storage: setJson', async (t) => { - const {localStorage} = globalThis; + const {localStorage} = global; const data = { hello: 'world', }; @@ -66,42 +66,42 @@ test('cloudcmd: client: storage: setJson', async (t) => { const expected = stringify(data); const setItem = stub(); - globalThis.localStorage = { + global.localStorage = { setItem, }; await storage.setJson('hello', data); - globalThis.localStorage = localStorage; + global.localStorage = localStorage; t.calledWith(setItem, ['hello', expected]); t.end(); }); test('cloudcmd: client: storage: remove', async (t) => { - const {localStorage} = globalThis; + const {localStorage} = global; const removeItem = stub(); - globalThis.localStorage = { + global.localStorage = { removeItem, }; await storage.remove('hello'); - globalThis.localStorage = localStorage; + global.localStorage = localStorage; t.calledWith(removeItem, ['hello'], 'should call removeItem'); t.end(); }); test('cloudcmd: client: storage: clear', async (t) => { - const {localStorage} = globalThis; + const {localStorage} = global; const clear = stub(); - globalThis.localStorage = { + global.localStorage = { clear, }; await storage.clear(); - globalThis.localStorage = localStorage; + global.localStorage = localStorage; t.calledWithNoArgs(clear, 'should call clear'); t.end(); diff --git a/client/dom/upload-files.js b/client/dom/upload-files.js index a1206282..7ac460ea 100644 --- a/client/dom/upload-files.js +++ b/client/dom/upload-files.js @@ -5,10 +5,10 @@ const {eachSeries} = require('execon'); const wraptile = require('wraptile'); const load = require('./load'); -const Images = require('./images.mjs'); +const Images = require('./images'); const {alert} = require('./dialog'); -const {FS} = require('../../common/cloudfunc.mjs'); +const {FS} = require('../../common/cloudfunc'); const {getCurrentDirPath: getPathWhenRootEmpty} = require('.'); const loadFile = wraptile(_loadFile); diff --git a/client/get-json-from-file-table.js b/client/get-json-from-file-table.js new file mode 100644 index 00000000..e6fbc872 --- /dev/null +++ b/client/get-json-from-file-table.js @@ -0,0 +1,47 @@ +'use strict'; + +/* global DOM */ +const Info = DOM.CurrentInfo; + +/** + * Функция генерирует JSON из html-таблицы файлов и + * используеться при первом заходе в корень + */ +module.exports = () => { + const path = DOM.getCurrentDirPath(); + const infoFiles = Info.files || []; + + const notParent = (current) => { + const name = DOM.getCurrentName(current); + return name !== '..'; + }; + + const parse = (current) => { + const name = DOM.getCurrentName(current); + const size = DOM.getCurrentSize(current); + const owner = DOM.getCurrentOwner(current); + const mode = DOM.getCurrentMode(current); + const date = DOM.getCurrentDate(current); + const type = DOM.getCurrentType(current); + + return { + name, + size, + mode, + owner, + date, + type, + }; + }; + + const files = infoFiles + .filter(notParent) + .map(parse); + + const fileTable = { + path, + files, + }; + + return fileTable; +}; diff --git a/client/get-json-from-file-table.mjs b/client/get-json-from-file-table.mjs deleted file mode 100644 index ea90f366..00000000 --- a/client/get-json-from-file-table.mjs +++ /dev/null @@ -1,44 +0,0 @@ -/* global DOM */ -/** - * Функция генерирует JSON из html-таблицы файлов и - * используеться при первом заходе в корень - */ -export const getJsonFromFileTable = () => { - const Info = DOM.CurrentInfo; - const path = DOM.getCurrentDirPath(); - const infoFiles = Info.files || []; - - const files = infoFiles - .filter(notParent) - .map(parse); - - const fileTable = { - path, - files, - }; - - return fileTable; -}; - -const notParent = (current) => { - const name = DOM.getCurrentName(current); - return name !== '..'; -}; - -const parse = (current) => { - const name = DOM.getCurrentName(current); - const size = DOM.getCurrentSize(current); - const owner = DOM.getCurrentOwner(current); - const mode = DOM.getCurrentMode(current); - const date = DOM.getCurrentDate(current); - const type = DOM.getCurrentType(current); - - return { - name, - size, - mode, - owner, - date, - type, - }; -}; diff --git a/client/key/index.mjs b/client/key/index.js similarity index 92% rename from client/key/index.mjs rename to client/key/index.js index 64fd1bc3..403e81ff 100644 --- a/client/key/index.mjs +++ b/client/key/index.js @@ -1,17 +1,22 @@ -/* global CloudCmd, DOM */ -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'; +'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'); + +const Info = DOM.CurrentInfo; const Chars = fullstore(); -const toggleVim = (keyCode, overrides = {}) => { - const {_config, config} = overrides; +const toggleVim = (keyCode) => { + const {_config, config} = CloudCmd; if (keyCode === KEY.ESC) _config('vim', !config('vim')); @@ -24,16 +29,13 @@ Chars([]); const {assign} = Object; const binder = createBinder(); -const bind = () => { +module.exports = assign(binder, KEY); +module.exports.bind = () => { Events.addKey(listener, true); binder.setBind(); }; -export const Key = assign(binder, KEY, { - bind, -}); - -export const _listener = listener; +module.exports._listener = listener; function getChar(event) { /* @@ -53,14 +55,7 @@ function getChar(event) { return [symbol, char]; } -async function listener(event, overrides = {}) { - const { - config = CloudCmd.config, - _config = CloudCmd._config, - switchKey = _switchKey, - vim = _vim, - } = overrides; - +async function listener(event) { const {keyCode} = event; // strange chrome bug calles listener twice @@ -79,12 +74,8 @@ async function listener(event, overrides = {}) { if (!binder.isBind()) return; - toggleVim(keyCode, { - config, - _config, - }); - - const isVim = config('vim'); + toggleVim(keyCode); + const isVim = CloudCmd.config('vim'); if (!isVim && !isNumpad && !alt && !ctrl && !meta && (isBetween || symbol)) return setCurrentByChar(char, Chars); @@ -121,8 +112,7 @@ function fromCharCode(keyIdentifier) { return String.fromCharCode(hex); } -async function _switchKey(event) { - const Info = DOM.CurrentInfo; +async function switchKey(event) { let i; let isSelected; let prev; diff --git a/client/key/index.spec.js b/client/key/index.spec.js index d8167079..8ab7a960 100644 --- a/client/key/index.spec.js +++ b/client/key/index.spec.js @@ -3,23 +3,27 @@ require('css-modules-require-hook/preset'); const autoGlobals = require('auto-globals'); +const mockRequire = require('mock-require'); const supertape = require('supertape'); const {ESC} = require('./key'); - -const {Key, _listener} = require('./index.mjs'); - const {getDOM, getCloudCmd} = require('./vim/globals.fixture'); const test = autoGlobals(supertape); +const {reRequire, stopAll} = mockRequire; const {stub} = supertape; -globalThis.DOM = getDOM(); -globalThis.CloudCmd = getCloudCmd(); +global.DOM = getDOM(); +global.CloudCmd = getCloudCmd(); test('cloudcmd: client: key: enable vim', async (t) => { const vim = stub(); - const config = stub().returns(true); - const _config = stub(); + const {CloudCmd} = global; + const {config} = CloudCmd; + + CloudCmd.config = stub().returns(true); + CloudCmd._config = stub(); + mockRequire('./vim', vim); + const {_listener, setBind} = reRequire('.'); const event = { keyCode: ESC, @@ -27,14 +31,11 @@ test('cloudcmd: client: key: enable vim', async (t) => { altKey: false, }; - Key.setBind(); + setBind(); + await _listener(event); - await _listener(event, { - vim, - config, - _config, - switchKey: stub(), - }); + CloudCmd.config = config; + stopAll(); t.calledWith(vim, ['Escape', event]); t.end(); @@ -42,20 +43,25 @@ test('cloudcmd: client: key: enable vim', async (t) => { test('cloudcmd: client: key: disable vim', async (t) => { const _config = stub(); - const config = stub(); const event = { keyCode: ESC, key: 'Escape', altKey: false, }; - Key.setBind(); - await _listener(event, { - config, - _config, - switchKey: stub(), - }); + const {CloudCmd} = global; + const {config} = CloudCmd; - t.calledWith(_config, ['vim', true]); + global.CloudCmd.config = _config; + global.CloudCmd._config = _config; + + const {_listener, setBind} = reRequire('.'); + + setBind(); + await _listener(event); + + CloudCmd.config = config; + + t.calledWith(_config, ['vim']); t.end(); }); diff --git a/client/key/set-current-by-char.js b/client/key/set-current-by-char.js index ab9329f3..f54881a3 100644 --- a/client/key/set-current-by-char.js +++ b/client/key/set-current-by-char.js @@ -3,9 +3,9 @@ 'use strict'; const {escapeRegExp} = require('../../common/util'); +const Info = DOM.CurrentInfo; module.exports = function setCurrentByChar(char, charStore) { - const Info = DOM.CurrentInfo; let firstByName; let skipCount = 0; let setted = false; diff --git a/client/key/vim/find.js b/client/key/vim/find.js index d8b517c1..43b3d36a 100644 --- a/client/key/vim/find.js +++ b/client/key/vim/find.js @@ -1,6 +1,6 @@ 'use strict'; -const {fullstore} = require('fullstore'); +const fullstore = require('fullstore'); const limier = require('limier'); const searchStore = fullstore([]); diff --git a/client/key/vim/find.spec.js b/client/key/vim/find.spec.js index 74cc7fb1..1b36216b 100644 --- a/client/key/vim/find.spec.js +++ b/client/key/vim/find.spec.js @@ -5,7 +5,7 @@ const dir = './'; const {getDOM} = require('./globals.fixture'); -globalThis.DOM = getDOM(); +global.DOM = getDOM(); const {_next, _previous} = require(`${dir}find`); diff --git a/client/key/vim/index.js b/client/key/vim/index.js index ddfe49c3..7c94f773 100644 --- a/client/key/vim/index.js +++ b/client/key/vim/index.js @@ -9,43 +9,32 @@ const { selectFileNotParent, } = require('./set-current'); -module.exports = (key, event, overrides = {}) => { - const defaults = { - ...globalThis.DOM, - ...globalThis.CloudCmd, - }; - - const deps = { - ...defaults, - ...overrides, - }; - +const {Dialog} = DOM; + +const DEPS = { + ...DOM, + ...CloudCmd, +}; + +module.exports = async (key, event, deps = DEPS) => { const operations = getOperations(event, deps); - - vim(key, operations, deps); + await vim(key, operations); }; const getOperations = (event, deps) => { const { - Info = globalThis.DOM.CurrentInfo, - CloudCmd = globalThis.CloudCmd, + Info = DOM.CurrentInfo, Operation, unselectFiles, setCurrentFile, setCurrentByName, getCurrentName, - prompt = globalThis.DOM.Dialog.prompt, - preventDefault = event?.preventDefault?.bind(event), toggleSelectedFile, Buffer = {}, - createFindNext = _createFindNext, } = deps; return { - findNext: createFindNext({ - setCurrentByName, - }), escape: unselectFiles, remove: () => { @@ -111,8 +100,8 @@ const getOperations = (event, deps) => { }, find: async () => { - preventDefault(); - const [, value] = await prompt('Find', ''); + event.preventDefault(); + const [, value] = await Dialog.prompt('Find', ''); if (!value) return; @@ -123,6 +112,11 @@ const getOperations = (event, deps) => { setCurrentByName(result); }, + findNext: () => { + const name = finder.findNext(); + setCurrentByName(name); + }, + findPrevious: () => { const name = finder.findPrevious(); setCurrentByName(name); @@ -131,10 +125,3 @@ 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 6fcad11e..245e21cc 100644 --- a/client/key/vim/index.spec.js +++ b/client/key/vim/index.spec.js @@ -10,13 +10,13 @@ const pathVim = join(dir, 'vim'); const {getDOM, getCloudCmd} = require('./globals.fixture'); -globalThis.DOM = getDOM(); -globalThis.CloudCmd = getCloudCmd(); +global.DOM = getDOM(); +global.CloudCmd = getCloudCmd(); -const vim = require('./index.js'); +const vim = require(pathVim); const {assign} = Object; -const {DOM} = globalThis; +const {DOM} = global; const {Buffer} = DOM; const pathFind = join(dir, 'vim', 'find'); const {reRequire, stopAll} = mockRequire; @@ -520,26 +520,15 @@ test('cloudcmd: client: key: Enter', async (t) => { test('cloudcmd: client: key: /', (t) => { const preventDefault = stub(); const element = {}; - const Info = { - element, - files: [], - }; - const getCurrentName = stub().returns(''); + DOM.CurrentInfo.element = element; + DOM.getCurrentName = () => ''; - const event = { + vim('/', { preventDefault, - }; - - const prompt = stub().returns([]); - - vim('/', event, { - getCurrentName, - Info, - prompt, }); - t.calledWithNoArgs(preventDefault); + t.calledWithNoArgs(preventDefault, 'should call preventDefault'); t.end(); }); @@ -570,13 +559,17 @@ 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, { - createFindNext, - }); + vim('n', event); + + stopAll(); t.calledWithNoArgs(findNext, 'should call findNext'); t.end(); @@ -602,7 +595,7 @@ test('cloudcmd: client: key: N', (t) => { test('cloudcmd: client: key: make directory', async (t) => { const vim = reRequire(pathVim); - const {DOM} = globalThis; + const {DOM} = global; assign(DOM, { promptNewDir: stub(), @@ -622,7 +615,7 @@ test('cloudcmd: client: key: make directory', async (t) => { test('cloudcmd: client: key: make file', (t) => { const vim = reRequire(pathVim); - const {DOM} = globalThis; + const {DOM} = global; assign(DOM, { promptNewFile: stub(), @@ -641,30 +634,28 @@ test('cloudcmd: client: key: make file', (t) => { }); test('cloudcmd: client: vim: terminal', (t) => { - const CloudCmd = { + const {CloudCmd} = global; + + assign(CloudCmd, { Terminal: { show: stub(), }, - }; + }); const event = {}; - vim('t', event, { - CloudCmd, - }); - vim('t', event, { - CloudCmd, - }); + vim('t', event); + vim('t', event); t.calledWithNoArgs(CloudCmd.Terminal.show); t.end(); }); test('cloudcmd: client: vim: edit', async (t) => { - globalThis.DOM = getDOM(); - globalThis.CloudCmd = getCloudCmd(); + global.DOM = getDOM(); + global.CloudCmd = getCloudCmd(); - const {CloudCmd} = globalThis; + const {CloudCmd} = global; assign(CloudCmd, { EditFileVim: { diff --git a/client/key/vim/vim.js b/client/key/vim/vim.js index 175f36ac..99fe2acf 100644 --- a/client/key/vim/vim.js +++ b/client/key/vim/vim.js @@ -1,6 +1,6 @@ 'use strict'; -const {fullstore} = require('fullstore'); +const fullstore = require('fullstore'); const store = fullstore(''); const visual = fullstore(false); diff --git a/client/listeners/index.js b/client/listeners/index.js index c12c62ed..1e3c9edf 100644 --- a/client/listeners/index.js +++ b/client/listeners/index.js @@ -5,13 +5,12 @@ const exec = require('execon'); const itype = require('itype'); const currify = require('currify'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const clipboard = require('@cloudcmd/clipboard'); const getRange = require('./get-range'); const uploadFiles = require('../dom/upload-files'); -const {FS} = require('../../common/cloudfunc.mjs'); -const Events = require('#dom/events'); +const {FS} = require('../../common/cloudfunc'); const getIndex = currify(require('./get-index')); @@ -30,8 +29,10 @@ module.exports.init = async () => { ]); }; +CloudCmd.Listeners = module.exports; + const unselect = (event) => { - const isMac = /Mac/.test(globalThis.navigator.platform); + const isMac = /Mac/.test(window.navigator.platform); const { shiftKey, metaKey, @@ -49,6 +50,9 @@ const execAll = currify((funcs, event) => { fn(event); }); +const Info = DOM.CurrentInfo; +const {Events} = DOM; + const EventsFiles = { mousedown: exec.with(execIfNotUL, setCurrentFileByEvent), click: execAll([onClick, exec.with(execIfNotMobile, unselect)]), @@ -163,7 +167,6 @@ function getPathListener(panel) { } function isNoCurrent(panel) { - const Info = DOM.CurrentInfo; const infoPanel = Info.panel; if (!infoPanel) @@ -188,7 +191,6 @@ function decodePath(path) { } async function onPathElementClick(panel, event) { - const Info = DOM.CurrentInfo; event.preventDefault(); const element = event.target; @@ -244,7 +246,7 @@ function onClick(event) { } function toggleSelect(key, files) { - const isMac = /Mac/.test(globalThis.navigator.platform); + const isMac = /Mac/.test(window.navigator.platform); if (!key) throw Error('key should not be undefined!'); @@ -259,7 +261,6 @@ function toggleSelect(key, files) { } function changePanel(element) { - const Info = DOM.CurrentInfo; const {panel} = Info; const files = DOM.getByDataName('js-files', panel); const ul = getULElement(element); @@ -301,7 +302,6 @@ async function onTouch(event) { * in Chrome (HTML5) */ function onDragStart(event) { - const Info = DOM.CurrentInfo; const {prefixURL} = CloudCmd; const element = getLIElement(event.target); const {isDir} = Info; @@ -338,7 +338,6 @@ function getULElement(element) { } function setCurrentFileByEvent(event) { - const Info = DOM.CurrentInfo; const BUTTON_LEFT = 0; const key = { @@ -452,7 +451,7 @@ function dragndrop() { } function unload() { - Events.add(['unload', 'beforeunload'], (event) => { + DOM.Events.add(['unload', 'beforeunload'], (event) => { const {Key} = CloudCmd; const isBind = Key?.isBind(); @@ -481,8 +480,7 @@ function pop() { function resize() { Events.add('resize', () => { - const Info = DOM.CurrentInfo; - const is = globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH; + const is = window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH; if (!is) return; diff --git a/client/load-module.mjs b/client/load-module.js similarity index 81% rename from client/load-module.mjs rename to client/load-module.js index 7fc2c328..0ce0cc64 100644 --- a/client/load-module.mjs +++ b/client/load-module.js @@ -1,16 +1,18 @@ -/* global CloudCmd */ -import exec from 'execon'; -import {tryToCatch} from 'try-to-catch'; -import {js as loadJS} from 'load.js'; -import pascalCase from 'just-pascal-case'; +'use strict'; +/* global CloudCmd */ +const exec = require('execon'); +const tryToCatch = require('try-to-catch'); +const loadJS = require('load.js').js; + +const pascalCase = require('just-pascal-case'); const noJS = (a) => a.replace(/.js$/, ''); /** * function load modules * @params = {name, path, func, dobefore, arg} */ -export const loadModule = (params) => { +module.exports = function loadModule(params) { if (!params) return; @@ -49,7 +51,7 @@ export const loadModule = (params) => { const [e, a] = await tryToCatch(m); if (e) - return; + return console.error(e); return await a.show(...args); }; diff --git a/client/modules/cloud.js b/client/modules/cloud.js index 53a6d9e9..81498c4b 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.mjs'); +const Images = require('../dom/images'); const {log} = CloudCmd; const upload = currify(_upload); diff --git a/client/modules/config/index.js b/client/modules/config/index.js index db778cbc..759ea85d 100644 --- a/client/modules/config/index.js +++ b/client/modules/config/index.js @@ -8,16 +8,16 @@ const currify = require('currify'); const wraptile = require('wraptile'); const squad = require('squad'); const {promisify} = require('es6-promisify'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const load = require('load.js'); const createElement = require('@cloudcmd/create-element'); const input = require('./input'); -const Images = require('../../dom/images.mjs'); -const Events = require('#dom/events'); +const Images = require('../../dom/images'); +const Events = require('../../dom/events'); const Files = require('../../dom/files'); -const {getTitle} = require('../../../common/cloudfunc.mjs'); +const {getTitle} = require('../../../common/cloudfunc'); const {Dialog, setTitle} = DOM; const Name = 'Config'; diff --git a/client/modules/contact.js b/client/modules/contact.js index c6266de0..76a07d30 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.mjs'); +const Images = require('../dom/images'); const {Events} = DOM; const {Key} = CloudCmd; diff --git a/client/modules/edit-file-vim.js b/client/modules/edit-file-vim.js index 0edd203b..48cfd93e 100644 --- a/client/modules/edit-file-vim.js +++ b/client/modules/edit-file-vim.js @@ -3,7 +3,7 @@ /* global CloudCmd */ CloudCmd.EditFileVim = exports; -const Events = require('#dom/events'); +const Events = require('../dom/events'); const {Key} = CloudCmd; diff --git a/client/modules/edit-file.js b/client/modules/edit-file.js index 59050a3c..f631351a 100644 --- a/client/modules/edit-file.js +++ b/client/modules/edit-file.js @@ -4,7 +4,7 @@ CloudCmd.EditFile = exports; const Format = require('format-io'); -const {fullstore} = require('fullstore'); +const fullstore = require('fullstore'); const exec = require('execon'); const supermenu = require('supermenu'); diff --git a/client/modules/edit-names-vim.js b/client/modules/edit-names-vim.js index 0dbd92b2..266dc9dc 100644 --- a/client/modules/edit-names-vim.js +++ b/client/modules/edit-names-vim.js @@ -3,7 +3,7 @@ /* global CloudCmd */ CloudCmd.EditNamesVim = exports; -const Events = require('#dom/events'); +const Events = require('../dom/events'); const {Key} = CloudCmd; const ConfigView = { diff --git a/client/modules/edit-names.js b/client/modules/edit-names.js index 710ed7ea..c2eaac12 100644 --- a/client/modules/edit-names.js +++ b/client/modules/edit-names.js @@ -1,17 +1,21 @@ 'use strict'; -const {tryToCatch} = require('try-to-catch'); - /* global CloudCmd, DOM */ CloudCmd.EditNames = exports; +const currify = require('currify'); const exec = require('execon'); const supermenu = require('supermenu'); -const {multiRename} = require('multi-rename'); +const multiRename = require('multi-rename'); + +const reject = Promise.reject.bind(Promise); const Info = DOM.CurrentInfo; const {Dialog} = DOM; +const refresh = currify(_refresh); +const rename = currify(_rename); + let Menu; const ConfigView = { @@ -89,7 +93,7 @@ function setListeners() { DOM.Events.addOnce('contextmenu', element, setMenu); } -async function applyNames() { +function applyNames() { const dir = Info.dirPath; const from = getActiveNames(); const nameIndex = from.indexOf(Info.name); @@ -101,18 +105,18 @@ async function applyNames() { const root = CloudCmd.config('root'); - const response = rename(dir, from, to, root); - const [error] = await tryToCatch(refresh, to, nameIndex, response); - - if (error) - alert(error); + Promise + .resolve(root) + .then(rename(dir, from, to)) + .then(refresh(to, nameIndex)) + .catch(alert); } -function refresh(to, nameIndex, res) { - if (res.status === 404) { - const error = res.text(); - throw error; - } +function _refresh(to, nameIndex, res) { + if (res.status === 404) + return res + .text() + .then(reject); const currentName = to[nameIndex]; @@ -128,7 +132,7 @@ function getDir(root, dir) { return root + dir; } -function rename(path, from, to, root) { +function _rename(path, from, to, root) { const dir = getDir(root, path); const {prefix} = CloudCmd; @@ -168,8 +172,8 @@ function setMenu(event) { }; const menuData = { - 'Save Ctrl+S': async () => { - await applyNames(); + 'Save Ctrl+S': () => { + applyNames(); hide(); }, 'Go To Line Ctrl+G': () => { @@ -210,7 +214,6 @@ async function isChanged() { if (!editor.isChanged()) return; - const [cancel] = await Dialog.confirm(msg); - - !cancel && await applyNames(); + const [, names] = await Dialog.confirm(msg); + names && applyNames(); } diff --git a/client/modules/edit.js b/client/modules/edit.js index ecefaad8..441aaf62 100644 --- a/client/modules/edit.js +++ b/client/modules/edit.js @@ -5,10 +5,10 @@ const montag = require('montag'); const {promisify} = require('es6-promisify'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const createElement = require('@cloudcmd/create-element'); const load = require('load.js'); -const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc.mjs'); +const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc'); const {time, timeEnd} = require('../../common/util'); const getEditor = () => editor; diff --git a/client/modules/help.js b/client/modules/help.js index 242b7c16..785bb32c 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.mjs'); +const Images = require('../dom/images'); module.exports.init = () => { Images.show.load('top'); diff --git a/client/modules/konsole.js b/client/modules/konsole.js index ae5bc42c..6993380d 100644 --- a/client/modules/konsole.js +++ b/client/modules/konsole.js @@ -8,11 +8,11 @@ CloudCmd.Konsole = exports; const exec = require('execon'); const currify = require('currify'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const loadJS = require('load.js').js; const createElement = require('@cloudcmd/create-element'); -const Images = require('../dom/images.mjs'); +const Images = require('../dom/images'); const {Dialog, CurrentInfo: Info} = DOM; const rmLastSlash = (a) => a.replace(/\/$/, '') || '/'; diff --git a/client/modules/markdown.js b/client/modules/markdown.js index 9dc224af..6c5c3282 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.mjs'); +const Images = require('../dom/images'); const {Markdown} = require('../dom/rest'); const {alert} = require('../dom/dialog'); diff --git a/client/modules/menu/cloudmenu.mjs b/client/modules/menu/cloudmenu.mjs index 1fbe5165..b36ca828 100644 --- a/client/modules/menu/cloudmenu.mjs +++ b/client/modules/menu/cloudmenu.mjs @@ -14,7 +14,7 @@ export const createCloudMenu = async (fm, options, menuData) => { async function loadMenu() { if (CloudCmd.config('menu') === 'aleman') { - const {host, protocol} = globalThis.location; + const {host, protocol} = window.location; const url = `${protocol}//${host}/node_modules/aleman/menu/menu.js`; const {createMenu} = await import(/* webpackIgnore: true */url); diff --git a/client/modules/menu/index.js b/client/modules/menu/index.js index facb385a..bc2682f4 100644 --- a/client/modules/menu/index.js +++ b/client/modules/menu/index.js @@ -6,7 +6,7 @@ const exec = require('execon'); const wrap = require('wraptile'); const createElement = require('@cloudcmd/create-element'); -const {FS} = require('../../../common/cloudfunc.mjs'); +const {FS} = require('../../../common/cloudfunc'); const {getIdBySrc} = require('../../dom/load'); const RESTful = require('../../dom/rest'); @@ -109,7 +109,6 @@ function getOptions({type}) { const options = { icon: true, - infiniteScroll: false, beforeClose: Key.setBind, beforeHide: Key.setBind, beforeShow: exec.with(beforeShow, func), diff --git a/client/modules/operation/index.mjs b/client/modules/operation/index.js similarity index 93% rename from client/modules/operation/index.mjs rename to client/modules/operation/index.js index 8e8149e5..bf19bda8 100644 --- a/client/modules/operation/index.mjs +++ b/client/modules/operation/index.js @@ -1,20 +1,28 @@ -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'; +/* global CloudCmd */ +/* global Util */ +/* global DOM */ +/* global fileop */ -const {DOM, CloudCmd} = globalThis; +'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 removeQuery = (a) => a.replace(/\?.*/, ''); const Name = 'Operation'; +CloudCmd[Name] = exports; + const {config} = CloudCmd; const {Dialog, Images} = DOM; @@ -45,7 +53,7 @@ const noFilesCheck = () => { return is; }; -export const init = promisify((callback) => { +module.exports.init = promisify((callback) => { showLoad(); exec.series([ @@ -84,7 +92,7 @@ const onConnect = currify((fn, operator) => { async function initOperations(prefix, socketPrefix, fn) { socketPrefix = `${socketPrefix}/fileop`; - const operator = await globalThis.fileop({ + const operator = await fileop({ prefix, socketPrefix, }); @@ -190,11 +198,11 @@ function getPacker(type) { return packTarFn; } -export const hide = () => { +module.exports.hide = () => { CloudCmd.View.hide(); }; -export const show = (operation, data) => { +module.exports.show = (operation, data) => { if (!Loaded) return; @@ -497,14 +505,8 @@ async function prompt(msg, to, names) { return await Dialog.prompt(msg, to); } -globalThis.CloudCmd[Name] = { - init, - hide, - show, -}; - async function loadAll() { - const {prefix} = globalThis.CloudCmd; + const {prefix} = CloudCmd; const file = `${prefix}/fileop/fileop.js`; const [error] = await tryToCatch(load.js, file); diff --git a/client/modules/operation/remove-extension.js b/client/modules/operation/remove-extension.js index 5e98727d..a8389af3 100644 --- a/client/modules/operation/remove-extension.js +++ b/client/modules/operation/remove-extension.js @@ -9,10 +9,10 @@ module.exports = (name) => { }; function getExtension(name) { - if (name.endsWith('.tar.gz')) + if (/\.tar\.gz$/.test(name)) return '.tar.gz'; - if (name.endsWith('.tar.bz2')) + if (/\.tar\.bz2$/.test(name)) return '.tar.bz2'; return getExt(name); diff --git a/client/modules/operation/set-listeners.mjs b/client/modules/operation/set-listeners.js similarity index 85% rename from client/modules/operation/set-listeners.mjs rename to client/modules/operation/set-listeners.js index d5052cff..495cd04b 100644 --- a/client/modules/operation/set-listeners.mjs +++ b/client/modules/operation/set-listeners.js @@ -1,11 +1,14 @@ +'use strict'; + /* global DOM */ -import forEachKey from 'for-each-key'; -import wraptile from 'wraptile'; -import format from './format.js'; +const forEachKey = require('for-each-key'); + +const wraptile = require('wraptile'); +const format = require('./format'); const {Dialog, Images} = DOM; -export const setListeners = (options) => (emitter) => { +module.exports = (options) => (emitter) => { const { operation, callback, @@ -40,13 +43,10 @@ export const setListeners = (options) => (emitter) => { operation, })); - let noProgress = true; - const listeners = { progress: (value) => { done = value === 100; progress.setProgress(value); - noProgress = false; }, end: () => { @@ -54,7 +54,7 @@ export const setListeners = (options) => (emitter) => { forEachKey(removeListener, listeners); progress.remove(); - if (lastError || done || noProgress) + if (lastError || done) callback(); }, diff --git a/client/modules/polyfill.js b/client/modules/polyfill.js index ff0e59c5..f5e7dc7a 100644 --- a/client/modules/polyfill.js +++ b/client/modules/polyfill.js @@ -1,20 +1,10 @@ 'use strict'; +/* global DOM */ require('domtokenlist-shim'); -const _scrollIntoViewIfNeeded = require('scroll-into-view-if-needed'); +const scrollIntoViewIfNeeded = require('scroll-into-view-if-needed'); -globalThis.DOM = globalThis.DOM || {}; - -const scrollIntoViewIfNeeded = (el, overrides = {}) => { - const { - scroll = _scrollIntoViewIfNeeded, - } = overrides; - - return scroll(el, { - block: 'nearest', - }); -}; - -globalThis.DOM.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded; -module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded; +DOM.scrollIntoViewIfNeeded = (el) => scrollIntoViewIfNeeded(el, { + block: 'nearest', +}); diff --git a/client/modules/polyfill.spec.js b/client/modules/polyfill.spec.js index 79d9dfda..67149e46 100644 --- a/client/modules/polyfill.spec.js +++ b/client/modules/polyfill.spec.js @@ -1,15 +1,24 @@ 'use strict'; const {test, stub} = require('supertape'); -const {scrollIntoViewIfNeeded} = require('./polyfill'); + +const mockRequire = require('mock-require'); + +const {stopAll} = mockRequire; test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => { + const {DOM} = global; const scroll = stub(); const el = {}; - scrollIntoViewIfNeeded(el, { - scroll, - }); + global.DOM = {}; + + mockRequire('scroll-into-view-if-needed', scroll); + mockRequire.reRequire('./polyfill'); + + global.DOM.scrollIntoViewIfNeeded(el); + mockRequire.stop('scroll-into-view-if-neaded'); + global.DOM = DOM; const args = [ el, { @@ -17,6 +26,8 @@ test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => { }, ]; + stopAll(); + t.calledWith(scroll, args, 'should call scrollIntoViewIfNeaded'); t.end(); }); diff --git a/client/modules/terminal-run.js b/client/modules/terminal-run.js index 4dcf24ac..3e4b857d 100644 --- a/client/modules/terminal-run.js +++ b/client/modules/terminal-run.js @@ -2,15 +2,15 @@ /* global CloudCmd, gritty */ const {promisify} = require('es6-promisify'); -const {tryToCatch} = require('try-to-catch'); -const {fullstore} = require('fullstore'); +const tryToCatch = require('try-to-catch'); +const fullstore = require('fullstore'); require('../../css/terminal.css'); const exec = require('execon'); const load = require('load.js'); const DOM = require('../dom'); -const Images = require('../dom/images.mjs'); +const Images = require('../dom/images'); const {Dialog} = DOM; const {Key, config} = CloudCmd; @@ -33,7 +33,7 @@ const loadAll = async () => { const [e] = await tryToCatch(load.parallel, [js, css]); if (e) { - const src = e.target.src.replace(globalThis.location.href, ''); + const src = e.target.src.replace(window.location.href, ''); return Dialog.alert(`file ${src} could not be loaded`); } diff --git a/client/modules/terminal.js b/client/modules/terminal.js index 6a355560..d36b700a 100644 --- a/client/modules/terminal.js +++ b/client/modules/terminal.js @@ -2,14 +2,14 @@ /* global CloudCmd */ /* global gritty */ -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); require('../../css/terminal.css'); const exec = require('execon'); const load = require('load.js'); const DOM = require('../dom'); -const Images = require('../dom/images.mjs'); +const Images = require('../dom/images'); const loadParallel = load.parallel; @@ -32,7 +32,7 @@ const loadAll = async () => { const [e] = await tryToCatch(loadParallel, [js, css]); if (e) { - const src = e.target.src.replace(globalThis.location.href, ''); + const src = e.target.src.replace(window.location.href, ''); return Dialog.alert(`file ${src} could not be loaded`); } diff --git a/client/modules/upload.js b/client/modules/upload.js index 23fbdaf5..63a2cd0f 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.mjs'); +const Images = require('../dom/images'); 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 1dba5d23..47fbee39 100644 --- a/client/modules/user-menu/index.js +++ b/client/modules/user-menu/index.js @@ -5,14 +5,14 @@ require('../../../css/user-menu.css'); const currify = require('currify'); const wraptile = require('wraptile'); -const {fullstore} = require('fullstore'); +const fullstore = require('fullstore'); const load = require('load.js'); const createElement = require('@cloudcmd/create-element'); -const {tryCatch} = require('try-catch'); -const {tryToCatch} = require('try-to-catch'); +const tryCatch = require('try-catch'); +const tryToCatch = require('try-to-catch'); const {codeFrameColumns} = require('@babel/code-frame'); -const Images = require('../../dom/images.mjs'); +const Images = require('../../dom/images'); const Dialog = require('../../dom/dialog'); const getUserMenu = require('./get-user-menu'); const navigate = require('./navigate'); diff --git a/client/modules/user-menu/navigate.js b/client/modules/user-menu/navigate.js index a87801ed..445d47d1 100644 --- a/client/modules/user-menu/navigate.js +++ b/client/modules/user-menu/navigate.js @@ -1,6 +1,6 @@ 'use strict'; -const {fullstore} = require('fullstore'); +const fullstore = require('fullstore'); const { J, diff --git a/client/modules/view/get-type.js b/client/modules/view/get-type.js index 9fc1df1d..3ac092e0 100644 --- a/client/modules/view/get-type.js +++ b/client/modules/view/get-type.js @@ -5,7 +5,7 @@ const testRegExp = currify((name, reg) => reg.test(name)); const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i'); const isPDF = (a) => /\.pdf$/i.test(a); -const isHTML = (a) => a.endsWith('.html'); +const isHTML = (a) => /\.html$/.test(a); const isMarkdown = (a) => /.\.md$/.test(a); module.exports = (name) => { diff --git a/client/modules/view/index.js b/client/modules/view/index.js index 89d68ee4..c6d34d18 100644 --- a/client/modules/view/index.js +++ b/client/modules/view/index.js @@ -2,22 +2,19 @@ 'use strict'; -const CloudCmd = globalThis.CloudCmd || {}; -const DOM = globalThis.DOM || {}; - require('../../../css/view.css'); const rendy = require('rendy'); const currify = require('currify'); const wraptile = require('wraptile'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const load = require('load.js'); -const _modal = require('@cloudcmd/modal'); -const _createElement = require('@cloudcmd/create-element'); +const modal = require('@cloudcmd/modal'); +const createElement = require('@cloudcmd/create-element'); const {time} = require('../../../common/util'); -const {FS} = require('../../../common/cloudfunc.mjs'); +const {FS} = require('../../../common/cloudfunc'); const { isImage, @@ -26,8 +23,8 @@ const { } = require('./types'); const Files = require('../../dom/files'); -const Events = require('#dom/events'); -const Images = require('../../dom/images.mjs'); +const Events = require('../../dom/events'); +const Images = require('../../dom/images'); const {encode} = require('../../../common/entity'); const isString = (a) => typeof a === 'string'; @@ -116,7 +113,7 @@ async function show(data, options = {}) { if (!options || options.bindKeys !== false) Events.addKey(listener); - El = _createElement('div', { + El = createElement('div', { className: 'view', notAppend: true, }); @@ -129,7 +126,7 @@ async function show(data, options = {}) { else El.append(data); - _modal.open(El, initConfig(options)); + modal.open(El, initConfig(options)); return; } @@ -160,11 +157,7 @@ async function show(data, options = {}) { } module.exports._createIframe = createIframe; -function createIframe(src, overrides = {}) { - const { - createElement = _createElement, - } = overrides; - +function createIframe(src) { const element = createElement('iframe', { src, width: '100%', @@ -179,8 +172,7 @@ function createIframe(src, overrides = {}) { } module.exports._viewHtml = viewHtml; -function viewHtml(src, overrides = {}) { - const {modal = _modal} = overrides; +function viewHtml(src) { modal.open(createIframe(src), Config); } @@ -192,7 +184,7 @@ function viewPDF(src) { if (CloudCmd.config('showFileName')) options.title = Info.name; - _modal.open(element, options); + modal.open(element, options); } async function viewMedia(path) { @@ -213,7 +205,7 @@ async function viewMedia(path) { }, }; - _modal.open(element, allConfig); + modal.open(element, allConfig); } async function viewFile() { @@ -229,7 +221,7 @@ async function viewFile() { options.title = Info.name; El.append(element); - _modal.open(El, options); + modal.open(El, options); } const copy = (a) => assign({}, a); @@ -261,7 +253,7 @@ function initConfig(options) { } function hide() { - _modal.close(); + modal.close(); } function viewImage(path, prefixURL) { @@ -294,7 +286,7 @@ function viewImage(path, prefixURL) { ...imageConfig, }; - _modal.open(titles, config); + modal.open(titles, config); } async function getMediaElement(src) { @@ -319,7 +311,7 @@ async function getMediaElement(src) { name, }); - const element = _createElement('div', { + const element = createElement('div', { innerHTML, }); diff --git a/client/modules/view/index.spec.js b/client/modules/view/index.spec.js index cc46d07d..83e6b476 100644 --- a/client/modules/view/index.spec.js +++ b/client/modules/view/index.spec.js @@ -3,20 +3,22 @@ require('css-modules-require-hook/preset'); const autoGlobals = require('auto-globals'); -const {stub} = require('@cloudcmd/stub'); - +const stub = require('@cloudcmd/stub'); +const mockRequire = require('mock-require'); const test = autoGlobals(require('supertape')); -const { - _initConfig, - _viewHtml, - _Config, - _createIframe, -} = require('.'); +const {reRequire, stopAll} = mockRequire; test('cloudcmd: client: view: initConfig', (t) => { let config; let i = 0; + const {CloudCmd, DOM} = global; + + global.CloudCmd = {}; + global.DOM = {}; + + const {_initConfig} = reRequire('.'); + const afterClose = () => ++i; const options = { afterClose, @@ -28,32 +30,54 @@ test('cloudcmd: client: view: initConfig', (t) => { config = _initConfig(options); config.afterClose(); + global.CloudCmd = CloudCmd; + global.DOM = DOM; + t.equal(i, 2, 'should not change default config'); t.end(); }); test('cloudcmd: client: view: initConfig: no options', (t) => { + const {CloudCmd, DOM} = global; + + global.CloudCmd = {}; + global.DOM = {}; + + const {_initConfig} = reRequire('.'); const config = _initConfig(); + global.CloudCmd = CloudCmd; + global.DOM = DOM; + t.equal(typeof config, 'object'); t.end(); }); test('cloudcmd: client: view: html', (t) => { + const {CloudCmd, DOM} = global; + + global.CloudCmd = {}; + global.DOM = {}; const open = stub(); - const modal = { + + mockRequire('@cloudcmd/modal', { open, - }; + }); + + const {_viewHtml, _Config} = reRequire('.'); const src = '/hello.html'; - _viewHtml(src, { - modal, - }); + _viewHtml(src); + + global.CloudCmd = CloudCmd; + global.DOM = DOM; const [first] = open.args; const [arg] = first; + stopAll(); + t.deepEqual(first, [arg, _Config]); t.end(); }); @@ -65,11 +89,12 @@ test('cloudcmd: client: view: createIframe', (t) => { }; const createElement = stub().returns(el); - const src = '/hello.html'; - _createIframe(src, { - createElement, - }); + mockRequire('@cloudcmd/create-element', createElement); + const {_createIframe} = reRequire('.'); + + const src = '/hello.html'; + _createIframe(src); const expected = { src, @@ -77,6 +102,8 @@ test('cloudcmd: client: view: createIframe', (t) => { width: '100%', }; + stopAll(); + t.calledWith(createElement, ['iframe', expected]); t.end(); }); @@ -89,10 +116,13 @@ test('cloudcmd: client: view: createIframe: returns', (t) => { const createElement = stub().returns(el); + mockRequire('@cloudcmd/create-element', createElement); + const {_createIframe} = reRequire('.'); + const src = '/hello.html'; - const result = _createIframe(src, { - createElement, - }); + const result = _createIframe(src); + + stopAll(); t.equal(result, el); t.end(); diff --git a/client/modules/view/types.js b/client/modules/view/types.js index d94dd971..e36fec4d 100644 --- a/client/modules/view/types.js +++ b/client/modules/view/types.js @@ -7,7 +7,7 @@ const testRegExp = currify((name, reg) => reg.test(name)); const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i'); const isPDF = (a) => /\.pdf$/i.test(a); -const isHTML = (a) => a.endsWith('.html'); +const isHTML = (a) => /\.html$/.test(a); const isMarkdown = (a) => /.\.md$/.test(a); module.exports.getType = async (path) => { diff --git a/client/modules/view/types.spec.js b/client/modules/view/types.spec.js index d8004f9e..52ed5bad 100644 --- a/client/modules/view/types.spec.js +++ b/client/modules/view/types.spec.js @@ -22,9 +22,12 @@ test('cloudcmd: client: view: types: detectType', async (t) => { headers: [], }); - globalThis.fetch = fetch; + const originalFetch = global.fetch; + + global.fetch = fetch; await _detectType('/hello'); + global.fetch = originalFetch; const expected = ['/hello', { method: 'HEAD', }]; @@ -34,13 +37,17 @@ test('cloudcmd: client: view: types: detectType', async (t) => { }); test('cloudcmd: client: view: types: detectType: found', async (t) => { - globalThis.fetch = stub().returns({ + const originalFetch = global.fetch; + + global.fetch = stub().returns({ headers: [ ['content-type', 'image/png'], ], }); const result = await _detectType('/hello'); + global.fetch = originalFetch; + t.equal(result, '.png'); t.end(); }); diff --git a/client/sort.js b/client/sort.js new file mode 100644 index 00000000..cf9fb4a4 --- /dev/null +++ b/client/sort.js @@ -0,0 +1,31 @@ +'use strict'; + +/* global CloudCmd */ +const DOM = require('./dom'); + +const Info = DOM.CurrentInfo; +const {sort, order} = CloudCmd; +const position = DOM.getPanelPosition(); +let sortPrevious = sort[position]; + +const {getPanel} = DOM; + +CloudCmd.sortPanel = (name, panel = getPanel()) => { + const position = panel.dataset.name.replace('js-', ''); + + if (name !== sortPrevious) + order[position] = 'asc'; + else if (order[position] === 'asc') + order[position] = 'desc'; + else + order[position] = 'asc'; + + sortPrevious = name; + sort[position] = name; + const noCurrent = position !== Info.panelPosition; + + CloudCmd.refresh({ + panel, + noCurrent, + }); +}; diff --git a/client/sort.mjs b/client/sort.mjs deleted file mode 100644 index 73539c81..00000000 --- a/client/sort.mjs +++ /dev/null @@ -1,36 +0,0 @@ -/* global CloudCmd */ -import {fullstore} from 'fullstore'; -import DOM from './dom/index.js'; - -const sortPrevious = fullstore(); - -const {getPanel} = DOM; - -export const initSortPanel = () => { - const {sort} = CloudCmd; - const position = DOM.getPanelPosition(); - - sortPrevious(sort[position]); -}; - -export const sortPanel = (name, panel = getPanel()) => { - const {sort, order} = CloudCmd; - const Info = DOM.CurrentInfo; - const position = panel.dataset.name.replace('js-', ''); - - if (name !== sortPrevious()) - order[position] = 'asc'; - else if (order[position] === 'asc') - order[position] = 'desc'; - else - order[position] = 'asc'; - - sortPrevious(name); - sort[position] = name; - const noCurrent = position !== Info.panelPosition; - - CloudCmd.refresh({ - panel, - noCurrent, - }); -}; diff --git a/client/sw/register.js b/client/sw/register.js index 0e6db472..12431a82 100644 --- a/client/sw/register.js +++ b/client/sw/register.js @@ -1,6 +1,6 @@ 'use strict'; -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); module.exports.registerSW = registerSW; module.exports.unregisterSW = unregisterSW; diff --git a/client/sw/register.spec.js b/client/sw/register.spec.js index 9b42a0e6..c4368c94 100644 --- a/client/sw/register.spec.js +++ b/client/sw/register.spec.js @@ -3,18 +3,14 @@ const autoGlobals = require('auto-globals'); const tape = require('supertape'); -const {stub} = require('@cloudcmd/stub'); - -const {tryCatch} = require('try-catch'); -const { - listenSW, - registerSW, - unregisterSW, -} = require('./register'); +const stub = require('@cloudcmd/stub'); +const tryCatch = require('try-catch'); +const {reRequire} = require('mock-require'); const test = autoGlobals(tape); test('sw: listen', (t) => { + const {listenSW} = reRequire('./register'); const addEventListener = stub(); const sw = { addEventListener, @@ -27,6 +23,7 @@ test('sw: listen', (t) => { }); test('sw: lesten: no sw', (t) => { + const {listenSW} = reRequire('./register'); const [e] = tryCatch(listenSW, null, 'hello', 'world'); t.notOk(e, 'should not throw'); @@ -34,6 +31,8 @@ test('sw: lesten: no sw', (t) => { }); test('sw: register: registerSW: no serviceWorker', async (t, {navigator}) => { + const {registerSW} = reRequire('./register'); + delete navigator.serviceWorker; await registerSW(); @@ -47,6 +46,8 @@ test('sw: register: registerSW: no https', async (t, {location, navigator}) => { location.protocol = 'http:'; + const {registerSW} = reRequire('./register'); + await registerSW(); t.notCalled(register, 'should not call register'); @@ -61,6 +62,8 @@ test('sw: register: registerSW: http', async (t, {location, navigator}) => { const {register} = navigator.serviceWorker; + const {registerSW} = reRequire('./register'); + await registerSW(); t.notCalled(register, 'should not call register'); @@ -76,6 +79,8 @@ test('sw: register: registerSW: https self-signed', async (t, {location, navigat const {register} = navigator.serviceWorker; register.throws(Error('Cannot register service worker!')); + const {registerSW} = reRequire('./register'); + const result = await registerSW(); t.notOk(result, 'should not throw'); @@ -86,6 +91,8 @@ test('sw: register: registerSW', async (t, {location, navigator}) => { location.hostname = 'localhost'; const {register} = navigator.serviceWorker; + const {registerSW} = reRequire('./register'); + await registerSW('/hello'); t.calledWith(register, ['/hello/sw.js'], 'should call register'); @@ -100,6 +107,8 @@ test('sw: register: unregisterSW', async (t, {location, navigator}) => { register.returns(serviceWorker); + const {unregisterSW} = reRequire('./register'); + await unregisterSW('/hello'); t.calledWith(register, ['/hello/sw.js'], 'should call register'); diff --git a/client/sw/sw.js b/client/sw/sw.js index e4cad253..15f3e0ad 100644 --- a/client/sw/sw.js +++ b/client/sw/sw.js @@ -2,7 +2,7 @@ const process = require('node:process'); const codegen = require('codegen.macro'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const currify = require('currify'); const isDev = process.env.NODE_ENV === 'development'; @@ -50,14 +50,14 @@ const getRequest = (a, request) => { return createRequest('/'); }; -globalThis.addEventListener('install', wait(onInstall)); -globalThis.addEventListener('fetch', respondWith(onFetch)); -globalThis.addEventListener('activate', wait(onActivate)); +self.addEventListener('install', wait(onInstall)); +self.addEventListener('fetch', respondWith(onFetch)); +self.addEventListener('activate', wait(onActivate)); async function onActivate() { console.info(`cloudcmd: sw: activate: ${NAME}`); - await globalThis.clients.claim(); + await self.clients.claim(); const keys = await caches.keys(); const deleteCache = caches.delete.bind(caches); @@ -67,7 +67,7 @@ async function onActivate() { async function onInstall() { console.info(`cloudcmd: sw: install: ${NAME}`); - await globalThis.skipWaiting(); + await self.skipWaiting(); } async function onFetch(event) { diff --git a/common/base64.js b/common/base64.js new file mode 100644 index 00000000..c1a82c17 --- /dev/null +++ b/common/base64.js @@ -0,0 +1,19 @@ +'use strict'; + +module.exports.btoa = (str) => { + if (typeof btoa === 'function') + return btoa(str); + + return Buffer + .from(str) + .toString('base64'); +}; + +module.exports.atob = (str) => { + if (typeof atob === 'function') + return atob(str); + + return Buffer + .from(str, 'base64') + .toString('binary'); +}; diff --git a/common/base64.spec.js b/common/base64.spec.js new file mode 100644 index 00000000..cc096ee2 --- /dev/null +++ b/common/base64.spec.js @@ -0,0 +1,55 @@ +'use strict'; + +const {test, stub} = require('supertape'); + +const {btoa, atob} = require('./base64'); + +test('btoa: browser', (t) => { + const btoaOriginal = global.btoa; + const btoaStub = stub(); + const str = 'hello'; + + global.btoa = btoaStub; + + btoa(str); + global.btoa = btoaOriginal; + + t.calledWith(btoaStub, [str], 'should call global.btoa'); + t.end(); +}); + +test('btoa: node', (t) => { + const str = 'hello'; + const expected = 'aGVsbG8='; + + const result = btoa(str); + + t.equal(result, expected, 'should encode base64'); + t.end(); +}); + +test('atob: browser', (t) => { + const atobOriginal = global.atob; + const atobStub = stub(); + + const str = 'hello'; + + global.atob = atobStub; + + atob(str); + + global.atob = atobOriginal; + + t.calledWith(atobStub, [str], 'should call global.btoa'); + t.end(); +}); + +test('atob: node', (t) => { + const str = 'aGVsbG8='; + const expected = 'hello'; + + const result = atob(str); + + t.equal(result, expected, 'should encode base64'); + t.end(); +}); diff --git a/common/callbackify.spec.js b/common/callbackify.spec.js index 0a46d9e9..1b742169 100644 --- a/common/callbackify.spec.js +++ b/common/callbackify.spec.js @@ -1,7 +1,7 @@ 'use strict'; const {promisify} = require('node:util'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const {test, stub} = require('supertape'); diff --git a/common/cloudfunc.mjs b/common/cloudfunc.js similarity index 87% rename from common/cloudfunc.mjs rename to common/cloudfunc.js index 00a48360..c906b548 100644 --- a/common/cloudfunc.mjs +++ b/common/cloudfunc.js @@ -1,16 +1,17 @@ -import rendy from 'rendy'; -import currify from 'currify'; -import store from 'fullstore'; -import {encode} from './entity.js'; +'use strict'; -export const getHeaderField = currify(_getHeaderField); +const rendy = require('rendy'); +const currify = require('currify'); +const store = require('fullstore'); +const {encode} = require('./entity'); +const {btoa} = require('./base64'); + +const getHeaderField = currify(_getHeaderField); /* КОНСТАНТЫ (общие для клиента и сервера)*/ /* название программы */ const NAME = 'Cloud Commander'; - -export const FS = '/fs'; - +const FS = '/fs'; const Path = store(); Path('/'); @@ -22,10 +23,14 @@ const filterOutDotFiles = ({showDotFiles}) => ({name}) => { return !name.startsWith('.'); }; -export const apiURL = '/api/v1'; -export const MAX_FILE_SIZE = 500 * 1024; +module.exports.FS = FS; +module.exports.apiURL = '/api/v1'; +module.exports.MAX_FILE_SIZE = 500 * 1024; +module.exports.getHeaderField = getHeaderField; +module.exports.getPathLink = getPathLink; +module.exports.getDotDot = getDotDot; -export const formatMsg = (msg, name, status) => { +module.exports.formatMsg = (msg, name, status) => { status = status || 'ok'; name = name || ''; @@ -39,7 +44,7 @@ export const formatMsg = (msg, name, status) => { * Функция возвращает заголовок веб страницы * @path */ -export const getTitle = (options) => { +module.exports.getTitle = (options) => { options = options || {}; const {path = Path(), name} = options; @@ -58,7 +63,7 @@ export const getTitle = (options) => { * возвращаеться массив каталогов * @param url - адрес каталога */ -export function getPathLink(url, prefix, template) { +function getPathLink(url, prefix, template) { if (!url) throw Error('url could not be empty!'); @@ -104,17 +109,17 @@ export function getPathLink(url, prefix, template) { return lines.join(''); } -export function _getDataName(name) { +const getDataName = (name) => { const encoded = btoa(encodeURI(name)); return `data-name="js-file-${encoded}" `; -} +}; /** * Функция строит таблицу файлв из JSON-информации о файлах * @param params - информация о файлах * */ -export const buildFromJSON = (params) => { +module.exports.buildFromJSON = (params) => { const { prefix, template, @@ -180,7 +185,7 @@ export const buildFromJSON = (params) => { name: '..', }); - const dataName = _getDataName('..'); + const dataName = getDataName('..'); const attribute = `draggable="true" ${dataName}`; /* Сохраняем путь к каталогу верхнего уровня*/ @@ -221,7 +226,7 @@ export const buildFromJSON = (params) => { attribute: getAttribute(file.type), }); - const dataName = _getDataName(file.name); + const dataName = getDataName(file.name); const attribute = `draggable="true" ${dataName}`; return rendy(templateFile, { @@ -257,8 +262,7 @@ function getAttribute(type) { return 'target="_blank" '; } -export const _getSize = getSize; - +module.exports._getSize = getSize; function getSize({size, type}) { if (type === 'directory') return '<dir>'; @@ -281,7 +285,7 @@ function _getHeaderField(sort, order, name) { return `${name}${arrow}`; } -export function getDotDot(path) { +function getDotDot(path) { // убираем последний слеш и каталог в котором мы сейчас находимся const lastSlash = path.substr(path, path.lastIndexOf('/')); const dotDot = lastSlash.substr(lastSlash, lastSlash.lastIndexOf('/')); diff --git a/common/cloudfunc.spec.mjs b/common/cloudfunc.spec.js similarity index 89% rename from common/cloudfunc.spec.mjs rename to common/cloudfunc.spec.js index 79ddfedf..e4e06dd7 100644 --- a/common/cloudfunc.spec.mjs +++ b/common/cloudfunc.spec.js @@ -1,15 +1,19 @@ -import {readFileSync} from 'node:fs'; -import test from 'supertape'; -import montag from 'montag'; -import * as cheerio from 'cheerio'; -import { +'use strict'; + +const {join} = require('node:path'); +const {readFileSync} = require('node:fs'); + +const test = require('supertape'); +const montag = require('montag'); +const cheerio = require('cheerio'); + +const { _getSize, getPathLink, buildFromJSON, - _getDataName, -} from './cloudfunc.mjs'; +} = require('./cloudfunc'); -const templatePath = new URL('../tmpl/fs', import.meta.url).pathname; +const templatePath = join(__dirname, '../tmpl/fs'); const template = { pathLink: readFileSync(`${templatePath}/pathLink.hbs`, 'utf8'), @@ -171,11 +175,3 @@ test('cloudfunc: buildFromJSON: showDotFiles: false', (t) => { t.equal(result, expected); t.end(); }); - -test('cloudfunc: _getDataName', (t) => { - const result = _getDataName('s'); - const expected = 'data-name="js-file-cw==" '; - - t.equal(result, expected); - t.end(); -}); diff --git a/common/datetime.spec.js b/common/datetime.spec.js index cc321d8a..db7a2483 100644 --- a/common/datetime.spec.js +++ b/common/datetime.spec.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('supertape'); -const {tryCatch} = require('try-catch'); +const tryCatch = require('try-catch'); const datetime = require('./datetime'); @@ -16,11 +16,11 @@ test('common: datetime', (t) => { }); test('common: datetime: no arg', (t) => { - const {Date} = globalThis; + const {Date} = global; let called = false; - globalThis.Date = class extends Date { + global.Date = class extends Date { constructor() { super(); called = true; @@ -29,7 +29,7 @@ test('common: datetime: no arg', (t) => { datetime(); - globalThis.Date = Date; + global.Date = Date; t.ok(called, 'should call new Date'); t.end(); diff --git a/common/try-to-promise-all.js b/common/try-to-promise-all.js index e839d27e..5c91b026 100644 --- a/common/try-to-promise-all.js +++ b/common/try-to-promise-all.js @@ -1,6 +1,6 @@ 'use strict'; -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const all = Promise.all.bind(Promise); module.exports = async (a) => { diff --git a/common/util.spec.js b/common/util.spec.js index f62cb4ca..ed15bd09 100644 --- a/common/util.spec.js +++ b/common/util.spec.js @@ -1,7 +1,8 @@ 'use strict'; const test = require('supertape'); -const {tryCatch} = require('try-catch'); +const {reRequire} = require('mock-require'); +const tryCatch = require('try-catch'); const Util = require('./util'); const { @@ -116,3 +117,15 @@ test('util: escapeRegExp', (t) => { t.equal(escapeRegExp('#hello'), '\\#hello'); t.end(); }); + +test('util: scope', (t) => { + global.window = {}; + + reRequire('./util'); + + t.pass('should set window in scope'); + + delete global.window; + + t.end(); +}); diff --git a/cssnano.config.mjs b/cssnano.config.js similarity index 63% rename from cssnano.config.mjs rename to cssnano.config.js index 44abaeaf..91ae5f81 100644 --- a/cssnano.config.mjs +++ b/cssnano.config.js @@ -1,7 +1,9 @@ -// used by OptimizeCssAssetsPlugin -import defaultPreset from 'cssnano-preset-default'; +'use strict'; -export default defaultPreset({ +// used by OptimizeCssAssetsPlugin +const defaultPreset = require('cssnano-preset-default'); + +module.exports = defaultPreset({ svgo: { plugins: [{ convertPathData: false, diff --git a/deno.json b/deno.json deleted file mode 100644 index 64c1bde2..00000000 --- a/deno.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "tasks": { - "start": "deno run -P=cloudcmd bin/cloudcmd.mjs" - }, - "permissions": { - "cloudcmd": { - "env": true, - "read": true, - "sys": true, - "net": true, - "run": true - } - } -} diff --git a/eslint.config.mjs b/eslint.config.mjs index cf7a6a2d..ad22cf4a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -19,6 +19,9 @@ export const match = { 'bin/cloudcmd.js': { 'no-console': 'off', }, + 'cssnano.config.js': { + 'n/no-extraneous-require': 'off', + }, }; export default defineConfig([ safeAlign, { @@ -33,7 +36,9 @@ 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 c9760653..22c6f40c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudcmd", - "version": "19.1.9", + "version": "19.0.8", "type": "commonjs", "author": "coderaiser (https://github.com/coderaiser)", "description": "File manager for the web with console and editor", @@ -89,7 +89,7 @@ "@cloudcmd/move-files": "^8.0.0", "@cloudcmd/read-files-sync": "^2.0.0", "@putout/cli-validate-args": "^2.0.0", - "aleman": "^1.16.5", + "aleman": "^1.14.3", "apart": "^2.0.0", "chalk": "^5.3.0", "compression": "^1.7.4", @@ -105,10 +105,10 @@ "execon": "^1.2.0", "express": "^5.1.0", "files-io": "^4.0.0", - "find-up": "^8.0.0", + "find-up": "^7.0.0", "for-each-key": "^2.0.0", "format-io": "^2.0.0", - "fullstore": "^4.0.0", + "fullstore": "^3.0.0", "http-auth": "^4.2.1", "inly": "^5.0.0", "jaguar": "^6.0.0", @@ -124,12 +124,12 @@ "object.omit": "^3.0.0", "once": "^1.4.0", "onezip": "^6.0.1", - "open": "^11.0.0", + "open": "^10.0.3", "package-json": "^10.0.0", "pipe-io": "^4.0.1", "ponse": "^7.0.0", "pullout": "^5.0.0", - "putout": "^41.0.0", + "putout": "^40.0.3", "redzip": "^3.0.0", "rendy": "^4.1.3", "restafary": "^12.0.0", @@ -140,8 +140,8 @@ "socket.io-client": "^4.0.1", "squad": "^3.0.0", "table": "^6.0.1", - "try-catch": "^4.0.4", - "try-to-catch": "^4.0.0", + "try-catch": "^3.0.0", + "try-to-catch": "^3.0.0", "tryrequire": "^3.0.0", "win32": "^7.0.0", "wraptile": "^3.0.0", @@ -156,9 +156,9 @@ "@cloudcmd/create-element": "^2.0.0", "@cloudcmd/modal": "^3.0.0", "@cloudcmd/olark": "^3.0.2", - "@cloudcmd/stub": "^5.0.0", + "@cloudcmd/stub": "^4.0.1", "@iocmd/wait": "^2.1.0", - "@putout/eslint-flat": "^4.0.0", + "@putout/eslint-flat": "^3.0.1", "@putout/plugin-cloudcmd": "^4.0.0", "@types/node-fetch": "^2.6.11", "auto-globals": "^4.0.0", @@ -170,29 +170,27 @@ "codegen.macro": "^4.0.0", "css-loader": "^7.1.2", "css-modules-require-hook": "^4.2.3", - "cssnano-preset-default": "^7.0.10", "domtokenlist-shim": "^1.2.0", "emitify": "^4.0.1", "eslint": "^9.23.0", "eslint-plugin-n": "^17.0.0-4", - "eslint-plugin-putout": "^30.0.0", - "globals": "^17.0.0", - "gritty": "^9.0.0", + "eslint-plugin-putout": "^28.0.0", + "globals": "^16.3.0", + "gritty": "^8.0.0", "gunzip-maybe": "^1.3.1", "html-webpack-plugin": "^5.6.3", "inherits": "^2.0.3", - "itype": "^3.0.1", "just-capitalize": "^3.2.0", "just-pascal-case": "^3.2.0", "limier": "^3.0.0", "load.js": "^3.0.0", - "madrun": "^12.1.0", + "madrun": "^11.0.0", "memfs": "^4.2.0", "mini-css-extract-plugin": "^2.9.2", "minor": "^1.2.2", "mock-require": "^3.0.1", "morgan": "^1.6.1", - "multi-rename": "^3.0.0", + "multi-rename": "^2.0.0", "nodemon": "^3.0.1", "optimize-css-assets-webpack-plugin": "^6.0.1", "path-browserify": "^1.0.1", @@ -201,7 +199,7 @@ "postcss": "^8.5.3", "process": "^0.11.10", "readjson": "^2.0.1", - "redlint": "^5.0.0", + "redlint": "^4.1.1", "request": "^2.76.0", "rimraf": "^6.0.1", "scroll-into-view-if-needed": "^3.0.4", @@ -209,7 +207,7 @@ "smalltalk": "^4.0.0", "style-loader": "^4.0.0", "supermenu": "^4.0.1", - "supertape": "^12.0.0", + "supertape": "^11.0.4", "tar-stream": "^3.0.0", "unionfs": "^4.0.0", "url-loader": "^4.0.0", @@ -218,9 +216,6 @@ "webpack-merge": "^6.0.1", "webpackbar": "^7.0.0" }, - "imports": { - "#dom/events": "./client/dom/events/index.mjs" - }, "engines": { "node": ">=22" }, diff --git a/server/cloudcmd.mjs b/server/cloudcmd.mjs index 2d1000cc..1305dd14 100644 --- a/server/cloudcmd.mjs +++ b/server/cloudcmd.mjs @@ -2,7 +2,7 @@ import path, {dirname, join} from 'node:path'; import {fileURLToPath} from 'node:url'; import process from 'node:process'; import fs from 'node:fs'; -import {fullstore} from 'fullstore'; +import fullstore from 'fullstore'; import currify from 'currify'; import apart from 'apart'; import ponse from 'ponse'; @@ -14,12 +14,12 @@ import dword from 'dword'; import deepword from 'deepword'; import nomine from 'nomine'; import fileop from '@cloudcmd/fileop'; -import * as cloudfunc from '../common/cloudfunc.mjs'; +import cloudfunc from '../common/cloudfunc.js'; import authentication from './auth.js'; -import {createConfig, configPath} from './config.mjs'; -import modulas from './modulas.mjs'; +import {createConfig, configPath} from './config.js'; +import modulas from './modulas.js'; import userMenu from './user-menu.mjs'; -import rest from './rest/index.mjs'; +import rest from './rest/index.js'; import route from './route.mjs'; import * as validate from './validate.mjs'; import prefixer from './prefixer.js'; diff --git a/server/columns.mjs b/server/columns.mjs index 570fe642..e463b901 100644 --- a/server/columns.mjs +++ b/server/columns.mjs @@ -2,12 +2,11 @@ import path, {dirname} from 'node:path'; import {fileURLToPath} from 'node:url'; import process from 'node:process'; import fs from 'node:fs'; -import {fullstore} from 'fullstore'; -import * as nanomemoizeDefault from 'nano-memoize'; +import fullstore from 'fullstore'; +import nanomemoizeDefault from 'nano-memoize'; import readFilesSync from '@cloudcmd/read-files-sync'; -const nanomemoize = nanomemoizeDefault.default.nanomemoize || nanomemoizeDefault.default; - +const {nanomemoize} = nanomemoizeDefault; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const isMap = (a) => /\.(map|js)$/.test(a); diff --git a/server/config.mjs b/server/config.js similarity index 91% rename from server/config.mjs rename to server/config.js index 3ec5bee8..3b180f09 100644 --- a/server/config.mjs +++ b/server/config.js @@ -1,12 +1,7 @@ -import {fileURLToPath} from 'node:url'; -import {dirname} from 'node:path'; -import {createRequire} from 'node:module'; +'use strict'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const require = createRequire(import.meta.url); const DIR_SERVER = `${__dirname}/`; - +const DIR_COMMON = '../common/'; const path = require('node:path'); const fs = require('node:fs'); @@ -16,17 +11,17 @@ const {homedir} = require('node:os'); const currify = require('currify'); const wraptile = require('wraptile'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const pullout = require('pullout'); const ponse = require('ponse'); const jonny = require('jonny'); const jju = require('jju'); const writejson = require('writejson'); -const {tryCatch} = require('try-catch'); +const tryCatch = require('try-catch'); const criton = require('criton'); const exit = require(`${DIR_SERVER}exit`); -const CloudFunc = require('../common/cloudfunc.mjs'); +const CloudFunc = require(`${DIR_COMMON}cloudfunc`); const isUndefined = (a) => typeof a === 'undefined'; const DIR = `${DIR_SERVER}../`; const HOME = homedir(); @@ -41,6 +36,7 @@ const key = (a) => Object .pop(); const ConfigPath = path.join(DIR, 'json/config.json'); +const ConfigHome = path.join(HOME, '.cloudcmd.json'); const connection = currify(_connection); const connectionWrapped = wraptile(_connection); @@ -69,7 +65,8 @@ function read(filename) { }; } -export const configPath = path.join(HOME, '.cloudcmd.json'); +module.exports.createConfig = createConfig; +module.exports.configPath = ConfigHome; const manageListen = currify((manage, socket, auth) => { if (!manage('configDialog')) @@ -87,7 +84,7 @@ function initWrite(filename, configManager) { return resolve; } -export function createConfig({configPath} = {}) { +function createConfig({configPath} = {}) { const config = {}; const changeEmitter = new Emitter(); @@ -240,8 +237,7 @@ function traverse([manage, json]) { } } -export const _cryptoPass = cryptoPass; - +module.exports._cryptoPass = cryptoPass; function cryptoPass(manage, json) { const algo = manage('algo'); diff --git a/server/config.spec.mjs b/server/config.spec.mjs index 3c6bbbc0..21cea8c0 100644 --- a/server/config.spec.mjs +++ b/server/config.spec.mjs @@ -1,7 +1,7 @@ import {createRequire} from 'node:module'; import {test, stub} from 'supertape'; -import {createConfig, _cryptoPass} from './config.mjs'; -import {apiURL} from '../common/cloudfunc.mjs'; +import {createConfig, _cryptoPass} from './config.js'; +import {apiURL} from '../common/cloudfunc.js'; import {connect} from '../test/before.mjs'; const require = createRequire(import.meta.url); diff --git a/server/distribute/export.spec.mjs b/server/distribute/export.spec.mjs index f234c8e7..e6dd1f8a 100644 --- a/server/distribute/export.spec.mjs +++ b/server/distribute/export.spec.mjs @@ -1,7 +1,7 @@ import {once} from 'node:events'; import test from 'supertape'; import io from 'socket.io-client'; -import * as Config from '../config.mjs'; +import Config from '../config.js'; import {connect} from '../../test/before.mjs'; const config = Config.createConfig(); diff --git a/server/distribute/import.mjs b/server/distribute/import.mjs index 4f503425..0a48d065 100644 --- a/server/distribute/import.mjs +++ b/server/distribute/import.mjs @@ -1,11 +1,11 @@ import currify from 'currify'; import wraptile from 'wraptile'; import squad from 'squad'; -import {fullstore} from 'fullstore'; +import fullstore from 'fullstore'; import io from 'socket.io-client'; import _forEachKey from 'for-each-key'; import log from './log.mjs'; -import * as env from '../env.mjs'; +import env from '../env.js'; const noop = () => {}; const forEachKey = currify(_forEachKey); @@ -58,7 +58,7 @@ const emitAuth = wraptile((importUrl, config, socket) => { const updateConfig = currify((config, data) => { for (const [key, value] of entries(data)) { - if (typeof env.parse(key) !== 'undefined') + if (typeof env(key) !== 'undefined') continue; config(key, value); diff --git a/server/distribute/import.spec.mjs b/server/distribute/import.spec.mjs index 35b0dd6c..9823aaff 100644 --- a/server/distribute/import.spec.mjs +++ b/server/distribute/import.spec.mjs @@ -1,7 +1,7 @@ import process from 'node:process'; import {promisify} from 'node:util'; import test from 'supertape'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import {connect} from '../../test/before.mjs'; import {createConfigManager} from '../cloudcmd.mjs'; import {distributeImport} from './import.mjs'; diff --git a/server/distribute/log.spec.mjs b/server/distribute/log.spec.mjs index eef535e3..50e4df11 100644 --- a/server/distribute/log.spec.mjs +++ b/server/distribute/log.spec.mjs @@ -1,6 +1,6 @@ import test from 'supertape'; import log from './log.mjs'; -import {createConfig} from '../config.mjs'; +import {createConfig} from '../config.js'; test('distribute: log: getMessage', (t) => { const e = 'hello'; diff --git a/server/env.mjs b/server/env.js similarity index 68% rename from server/env.mjs rename to server/env.js index 86865307..d2b4e32f 100644 --- a/server/env.mjs +++ b/server/env.js @@ -1,9 +1,12 @@ -import {env} from 'node:process'; -import snake from 'just-snake-case'; +'use strict'; + +const {env} = require('node:process'); +const snake = require('just-snake-case'); const up = (a) => a.toUpperCase(); -export const bool = (name) => { +module.exports = parse; +module.exports.bool = (name) => { const value = parse(name); if (value === 'true') @@ -19,9 +22,9 @@ export const bool = (name) => { return false; }; -export const parse = (name) => { +function parse(name) { const small = `cloudcmd_${snake(name)}`; const big = up(small); return env[big] || env[small]; -}; +} diff --git a/server/env.spec.js b/server/env.spec.js index d8f3faf6..36843cb2 100644 --- a/server/env.spec.js +++ b/server/env.spec.js @@ -2,7 +2,7 @@ const process = require('node:process'); const test = require('supertape'); -const env = require('./env.mjs'); +const env = require('./env'); test('cloudcmd: server: env: bool: upper case first', (t) => { const { diff --git a/server/markdown/index.spec.mjs b/server/markdown/index.spec.mjs index 563442af..2f51262e 100644 --- a/server/markdown/index.spec.mjs +++ b/server/markdown/index.spec.mjs @@ -1,7 +1,7 @@ import fs from 'node:fs'; import {join} from 'node:path'; import {promisify} from 'node:util'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import test from 'supertape'; import serveOnce from 'serve-once'; import markdown from './index.js'; diff --git a/server/modulas.mjs b/server/modulas.js similarity index 60% rename from server/modulas.mjs rename to server/modulas.js index 7d38df99..5cc1264c 100644 --- a/server/modulas.mjs +++ b/server/modulas.js @@ -1,9 +1,9 @@ -import deepmerge from 'deepmerge'; -import originalModules from '../json/modules.json' with { - type: 'json', -}; +'use strict'; -export default (modules) => { +const deepmerge = require('deepmerge'); +const originalModules = require('../json/modules'); + +module.exports = (modules) => { const result = deepmerge(originalModules, modules || {}); return (req, res, next) => { diff --git a/server/rest/index.mjs b/server/rest/index.js similarity index 89% rename from server/rest/index.mjs rename to server/rest/index.js index f5e6c8da..c133e2dc 100644 --- a/server/rest/index.mjs +++ b/server/rest/index.js @@ -1,21 +1,26 @@ -import path from 'node:path'; -import _fs from 'node:fs'; -import process from 'node:process'; -import jaguar from 'jaguar'; -import onezip from 'onezip'; -import inly from 'inly'; -import wraptile from 'wraptile'; -import currify from 'currify'; -import pullout from 'pullout'; -import json from 'jonny'; -import ponse from 'ponse'; -import copymitter from 'copymitter'; -import _moveFiles from '@cloudcmd/move-files'; -import root from '../root.js'; -import * as CloudFunc from '../../common/cloudfunc.mjs'; -import markdown from '../markdown/index.js'; -import info from './info.js'; +'use strict'; +const path = require('node:path'); +const _fs = require('node:fs'); + +const process = require('node:process'); + +const jaguar = require('jaguar'); +const onezip = require('onezip'); +const inly = require('inly'); +const wraptile = require('wraptile'); +const currify = require('currify'); +const pullout = require('pullout'); +const json = require('jonny'); +const ponse = require('ponse'); + +const copymitter = require('copymitter'); +const _moveFiles = require('@cloudcmd/move-files'); + +const root = require(`../root`); +const CloudFunc = require(`../../common/cloudfunc`); +const markdown = require(`../markdown/index.js`); +const info = require('./info'); const isUndefined = (a) => typeof a === 'undefined'; const isRootAll = (root, names) => names.some(isRootWin32(root)); const isString = (a) => typeof a === 'string'; @@ -32,7 +37,7 @@ const UserError = (msg) => { return error; }; -export default currify(({config, fs = _fs, moveFiles = _moveFiles}, request, response, next) => { +module.exports = currify(({config, fs = _fs, moveFiles = _moveFiles}, request, response, next) => { const name = ponse.getPathName(request); const regExp = RegExp(`^${apiURL}`); const is = regExp.test(name); @@ -81,7 +86,7 @@ function rest({fs, config, moveFiles}, request, response) { /** * getting data on method and command * - * @param params {name, method, body, request, response} + * @param params {name, method, body, requrest, response} * @param config {} * @param callback */ @@ -183,8 +188,7 @@ const getRenameMsg = (from, to) => { return msg; }; -export const _onPUT = onPUT; - +module.exports._onPUT = onPUT; function onPUT({name, fs, moveFiles, config, body}, callback) { checkPut(name, body, callback); @@ -364,10 +368,10 @@ const isRootWin32 = currify((root, path) => { return isWin32 && isRoot && isConfig; }); -export const _isRootWin32 = isRootWin32; -export const _isRootAll = isRootAll; +module.exports._isRootWin32 = isRootWin32; +module.exports._isRootAll = isRootAll; -export const _getWin32RootMsg = getWin32RootMsg; +module.exports._getWin32RootMsg = getWin32RootMsg; function getWin32RootMsg() { const message = 'Could not copy from/to root on windows!'; @@ -384,7 +388,7 @@ function parseData(data) { return json.stringify(data); } -export const _formatMsg = formatMsg; +module.exports._formatMsg = formatMsg; function formatMsg(msg, data, status) { const value = parseData(data); diff --git a/server/rest/index.spec.js b/server/rest/index.spec.js index 540356ad..84da6a71 100644 --- a/server/rest/index.spec.js +++ b/server/rest/index.spec.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('supertape'); -const {tryToCatch} = require('try-to-catch'); +const tryToCatch = require('try-to-catch'); const { _formatMsg, @@ -9,7 +9,7 @@ const { _isRootWin32, _isRootAll, _onPUT, -} = require('./index.mjs'); +} = require('.'); test('rest: formatMsg', (t) => { const result = _formatMsg('hello', 'world'); diff --git a/server/route.mjs b/server/route.mjs index fabde3fb..675a0b7a 100644 --- a/server/route.mjs +++ b/server/route.mjs @@ -6,13 +6,13 @@ import rendy from 'rendy'; import format from 'format-io'; import currify from 'currify'; import wraptile from 'wraptile'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import once from 'once'; import pipe from 'pipe-io'; import {contentType} from 'mime-types'; import root from './root.js'; import prefixer from './prefixer.js'; -import * as CloudFunc from '../common/cloudfunc.mjs'; +import CloudFunc from '../common/cloudfunc.js'; import Template from './template.js'; import {getColumns} from './columns.mjs'; import {getThemes} from './theme.mjs'; diff --git a/server/route.spec.mjs b/server/route.spec.mjs index bee216a8..433e0cdc 100644 --- a/server/route.spec.mjs +++ b/server/route.spec.mjs @@ -2,7 +2,7 @@ import path, {dirname} from 'node:path'; import {fileURLToPath} from 'node:url'; import {Readable} from 'node:stream'; import fs from 'node:fs'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import {test, stub} from 'supertape'; import serveOnce from 'serve-once'; import cloudcmd from './cloudcmd.mjs'; diff --git a/server/server.mjs b/server/server.mjs index dc61f522..096f9f40 100644 --- a/server/server.mjs +++ b/server/server.mjs @@ -3,7 +3,7 @@ import {promisify} from 'node:util'; import process from 'node:process'; import currify from 'currify'; import squad from 'squad'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import opn from 'open'; import express from 'express'; import {Server} from 'socket.io'; diff --git a/server/terminal.js b/server/terminal.js index 918ad1c4..67613f0a 100644 --- a/server/terminal.js +++ b/server/terminal.js @@ -1,6 +1,6 @@ 'use strict'; -const {tryCatch} = require('try-catch'); +const tryCatch = require('try-catch'); const noop = (req, res, next) => { next && next(); diff --git a/server/theme.mjs b/server/theme.mjs index 7e1f2da4..3e1b88bb 100644 --- a/server/theme.mjs +++ b/server/theme.mjs @@ -2,11 +2,10 @@ import path, {dirname} from 'node:path'; import {fileURLToPath} from 'node:url'; import process from 'node:process'; import fs from 'node:fs'; -import {fullstore} from 'fullstore'; -import * as nanomemoizeDefault from 'nano-memoize'; +import fullstore from 'fullstore'; +import nanomemoizeDefault from 'nano-memoize'; import readFilesSync from '@cloudcmd/read-files-sync'; -const nanomemoize = nanomemoizeDefault.default.nanomemoize || nanomemoizeDefault.default; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const isMap = (a) => /\.(map|js)$/.test(a); @@ -21,6 +20,8 @@ export const getThemes = ({isDev = _isDev()} = {}) => { return readFilesSyncMemo(isDev); }; +const {nanomemoize} = nanomemoizeDefault; + const readFilesSyncMemo = nanomemoize((isDev) => { const dist = getDist(isDev); const themesDir = path.join(__dirname, '..', dist, 'themes'); diff --git a/server/user-menu.mjs b/server/user-menu.mjs index 4eaa3a8c..1100e1f3 100644 --- a/server/user-menu.mjs +++ b/server/user-menu.mjs @@ -2,7 +2,7 @@ import {homedir} from 'node:os'; import {readFile as _readFile} from 'node:fs/promises'; import {join} from 'node:path'; import montag from 'montag'; -import {tryToCatch} from 'try-to-catch'; +import tryToCatch from 'try-to-catch'; import currify from 'currify'; import {putout, codeframe} from 'putout'; diff --git a/server/validate.mjs b/server/validate.mjs index 3699d081..4694ca16 100644 --- a/server/validate.mjs +++ b/server/validate.mjs @@ -1,5 +1,5 @@ import {statSync as _statSync} from 'node:fs'; -import {tryCatch} from 'try-catch'; +import tryCatch from 'try-catch'; import _exit from './exit.js'; import {getColumns as _getColumns} from './columns.mjs'; import {getThemes as _getThemes} from './theme.mjs'; diff --git a/server/validate.spec.mjs b/server/validate.spec.mjs index 568e55c2..800b4395 100644 --- a/server/validate.spec.mjs +++ b/server/validate.spec.mjs @@ -1,5 +1,5 @@ import {test, stub} from 'supertape'; -import {tryCatch} from 'try-catch'; +import tryCatch from 'try-catch'; import * as validate from './validate.mjs'; import cloudcmd from './cloudcmd.mjs'; diff --git a/static/user-menu.spec.js b/static/user-menu.spec.js index d46d4374..9c42302a 100644 --- a/static/user-menu.spec.js +++ b/static/user-menu.spec.js @@ -1,8 +1,8 @@ 'use strict'; const autoGlobals = require('auto-globals'); -const {stub} = require('@cloudcmd/stub'); -const {tryToCatch} = require('try-to-catch'); +const stub = require('@cloudcmd/stub'); +const tryToCatch = require('try-to-catch'); const wraptile = require('wraptile'); const defaultMenu = require('./user-menu'); diff --git a/test/common/cloudfunc.js b/test/common/cloudfunc.js index 6b2a052d..b74016fb 100644 --- a/test/common/cloudfunc.js +++ b/test/common/cloudfunc.js @@ -2,12 +2,12 @@ const fs = require('node:fs'); -const {tryCatch} = require('try-catch'); +const tryCatch = require('try-catch'); const test = require('supertape'); const readFilesSync = require('@cloudcmd/read-files-sync'); const {time, timeEnd} = require(`../../common/util`); -const CloudFunc = require('../../common/cloudfunc.mjs'); +const CloudFunc = require('../../common/cloudfunc.js'); const DIR = `${__dirname}/../../`; diff --git a/test/server/console.mjs b/test/server/console.mjs index 72984d50..266cce14 100644 --- a/test/server/console.mjs +++ b/test/server/console.mjs @@ -1,10 +1,16 @@ +import path, {dirname} from 'node:path'; import {once} from 'node:events'; +import {fileURLToPath} from 'node:url'; +import {createRequire} from 'node:module'; import test from 'supertape'; import io from 'socket.io-client'; import {connect} from '../before.mjs'; -import {createConfig} from '../../server/config.mjs'; -const configFn = createConfig(); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const require = createRequire(import.meta.url); +const configPath = path.join(__dirname, '../..', 'server', 'config'); +const configFn = require(configPath).createConfig(); test('cloudcmd: console: enabled', async (t) => { const config = { diff --git a/test/server/env.js b/test/server/env.js index 52f54b45..d794f945 100644 --- a/test/server/env.js +++ b/test/server/env.js @@ -2,11 +2,11 @@ const process = require('node:process'); const test = require('supertape'); -const env = require('../../server/env.mjs'); +const env = require('../../server/env'); test('env: small', (t) => { process.env.cloudcmd_hello = 'world'; - t.equal(env.parse('hello'), 'world', 'should parse string from env'); + t.equal(env('hello'), 'world', 'should parse string from env'); delete process.env.cloudcmd_hello; t.end(); @@ -14,7 +14,7 @@ test('env: small', (t) => { test('env: big', (t) => { process.env.CLOUDCMD_HELLO = 'world'; - t.equal(env.parse('hello'), 'world', 'should parse string from env'); + t.equal(env('hello'), 'world', 'should parse string from env'); delete process.env.CLOUDCMD_HELLO; t.end(); diff --git a/test/server/modulas.mjs b/test/server/modulas.mjs index c6281501..ed2f736f 100644 --- a/test/server/modulas.mjs +++ b/test/server/modulas.mjs @@ -4,14 +4,15 @@ import {fileURLToPath} from 'node:url'; import serveOnce from 'serve-once'; import {test, stub} from 'supertape'; import cloudcmd from '../../server/cloudcmd.mjs'; -import modulas from '../../server/modulas.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const require = createRequire(import.meta.url); const cloudcmdPath = join(__dirname, '..', '..'); + const modulesPath = join(cloudcmdPath, 'json', 'modules.json'); const localModules = require(modulesPath); +const modulas = require(`../../server/modulas`); const {request} = serveOnce(cloudcmd, { config: { diff --git a/test/server/show-config.js b/test/server/show-config.js index d0f3c8f6..3de1c170 100644 --- a/test/server/show-config.js +++ b/test/server/show-config.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('supertape'); -const {tryCatch} = require('try-catch'); +const tryCatch = require('try-catch'); const showConfig = require('../../server/show-config'); diff --git a/test/static.mjs b/test/static.mjs index 03a57e35..8d65c384 100644 --- a/test/static.mjs +++ b/test/static.mjs @@ -1,4 +1,3 @@ -import {Buffer} from 'node:buffer'; import serveOnce from 'serve-once'; import test from 'supertape'; import criton from 'criton'; diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..63a920f2 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,13 @@ +'use strict'; + +const {merge} = require('webpack-merge'); + +const htmlConfig = require('./.webpack/html'); +const cssConfig = require('./.webpack/css'); +const jsConfig = require('./.webpack/js'); + +module.exports = merge([ + jsConfig, + htmlConfig, + cssConfig, +]); diff --git a/webpack.config.mjs b/webpack.config.mjs deleted file mode 100644 index a1eb0da2..00000000 --- a/webpack.config.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import {merge} from 'webpack-merge'; -import * as htmlConfig from './.webpack/html.mjs'; -import cssConfig from './.webpack/css.mjs'; -import jsConfig from './.webpack/js.mjs'; - -export default merge([ - jsConfig, - htmlConfig, - cssConfig, -]);