diff --git a/.cloudcmd.menu.js b/.cloudcmd.menu.js index 0e0126b6..6c808d18 100644 --- a/.cloudcmd.menu.js +++ b/.cloudcmd.menu.js @@ -1,31 +1,45 @@ -export default { - 'F2 - Rename File': renameCurrent, - 'L - Lint': run('npm run lint'), - 'F - Fix Lint': run('npm run fix:lint'), - 'T - Test': run('npm run test'), - 'C - Coverage': run('npm run coverage'), - 'D - Build Dev': run('npm run build:client:dev'), - 'P - Build Prod': run('npm run build:client'), +'use strict'; + +module.exports = { + 'F2 - Rename file': async ({DOM}) => { + await DOM.renameCurrent(); + }, + 'L - Lint': async ({CloudCmd}) => { + const {TerminalRun} = CloudCmd; + await run(TerminalRun, 'npm run lint'); + }, + 'F - Fix Lint': async ({CloudCmd}) => { + const {TerminalRun} = CloudCmd; + await run(TerminalRun, 'npm run fix:lint'); + }, + 'T - Test': async ({CloudCmd}) => { + const {TerminalRun} = CloudCmd; + + await run(TerminalRun, 'npm run test'); + }, + 'C - Coverage': async ({CloudCmd}) => { + const {TerminalRun} = CloudCmd; + + await run(TerminalRun, 'npm run coverage'); + }, + 'D - Build Dev': async ({CloudCmd}) => { + const {TerminalRun} = CloudCmd; + + await run(TerminalRun, 'npm run build:client:dev'); + CloudCmd.refresh(); + }, + 'P - Build Prod': async ({CloudCmd}) => { + const {TerminalRun} = CloudCmd; + + await run(TerminalRun, 'npm run build:client'); + CloudCmd.refresh(); + }, }; -async function renameCurrent(DOM) { - await DOM.renameCurrent(); -} - -function run(command) { - return async ({CloudCmd, DOM}) => { - const {TerminalRun, config} = CloudCmd; - - const {CurrentInfo} = DOM; - const {dirPath} = CurrentInfo; - - const cwd = config('root') + dirPath; - - return await TerminalRun.show({ - cwd, - command, - closeMessage: 'Press any key to close Terminal', - autoClose: false, - }); - }; +async function run(TerminalRun, command) { + await TerminalRun.show({ + command, + closeMessage: 'Press any key to close Terminal', + autoClose: false, + }); } diff --git a/.github/workflows/docker-io.yml b/.github/workflows/docker-io.yml deleted file mode 100644 index 8cbeb042..00000000 --- a/.github/workflows/docker-io.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Docker IO -permissions: - contents: write -on: - - push -jobs: - buildx: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout - uses: actions/checkout@v5 - - uses: oven-sh/setup-bun@v2 - with: - bun-version: latest - - name: Use Node.js 24.x - uses: actions/setup-node@v6 - with: - node-version: 24.x - - name: Install Redrun - run: bun i redrun -g --no-save - - name: NPM Install - run: bun i --no-save - - name: Lint - run: redrun lint - - name: Build - id: build - run: | - redrun build - VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4) - echo "version=$VERSION" >> $GITHUB_OUTPUT - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - name: Login to DockerHub - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_TOKEN }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push io-image - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile.io - platforms: linux/amd64,linux/arm64 - push: true - tags: | - coderaiser/cloudcmd:io - coderaiser/cloudcmd:${{ steps.build.outputs.version }}-io - ghcr.io/${{ github.repository }}-io - ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-io diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5c81d368..376b0f08 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,6 +1,4 @@ name: Docker CI -permissions: - contents: write on: push: tags: @@ -31,25 +29,24 @@ jobs: id: build run: | redrun build - VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4) - echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "::set-output name=version::$(grep '"version":' package.json -m1 | cut -d\" -f4)" - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v4 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Login to GitHub Container Registry - uses: docker/login-action@v4 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push base-image - uses: docker/build-push-action@v7 + uses: docker/build-push-action@v5 with: context: . file: docker/Dockerfile @@ -61,28 +58,14 @@ jobs: ghcr.io/${{ github.repository }}:latest ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }} - name: Build and push alpine-image - uses: docker/build-push-action@v7 + uses: docker/build-push-action@v5 with: context: . file: docker/Dockerfile.alpine platforms: linux/amd64,linux/arm64 push: true tags: | - coderaiser/cloudcmd:alpine coderaiser/cloudcmd:latest-alpine coderaiser/cloudcmd:${{ steps.build.outputs.version }}-alpine ghcr.io/${{ github.repository }}:latest-alpine ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-alpine - - name: Build and push slim-image - uses: docker/build-push-action@v7 - with: - context: . - file: docker/Dockerfile.slim - platforms: linux/amd64,linux/arm64 - push: true - tags: | - coderaiser/cloudcmd:slim - coderaiser/cloudcmd:latest-slim - coderaiser/cloudcmd:${{ steps.build.outputs.version }}-slim - ghcr.io/${{ github.repository }}-slim - ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-slim diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 429e5568..1b392900 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,8 +1,6 @@ name: Node CI on: - push -permissions: - contents: write jobs: build: runs-on: ubuntu-latest @@ -13,7 +11,7 @@ jobs: node-version: - 22.x - 24.x - - 26.x + - 25.x steps: - uses: actions/checkout@v5 - uses: oven-sh/setup-bun@v2 @@ -24,19 +22,26 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: Install Redrun - run: bun i redrun madrun -g --no-save + run: bun i redrun -g --no-save - name: Install run: bun i --no-save - - name: Init Madrun - run: madrun --init - name: Lint run: redrun fix:lint - - name: Typos - uses: coderaiser/typos.ai@v1.1.8 + - uses: actions/cache@v4 with: - key: ${{ secrets.TYPOS_AI_KEY }} + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Typos Install + run: cargo install typos-cli || echo 'already installed' + - name: Typos + run: typos --write-changes - name: Commit fixes - uses: EndBug/add-and-commit@v10 + uses: EndBug/add-and-commit@v9 continue-on-error: true with: message: "chore: ${{ env.NAME }}: actions: lint ☘️" diff --git a/.gitignore b/.gitignore index 6aa86aa9..e6d5d021 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,19 @@ -*.swp -*.log -*.lock - -.nyc_output -.DS_Store -.idea - package-lock.json -npm-debug.log* - +yarn.lock +yarn-error.log node_modules +npm-debug.log* coverage + modules/execon modules/emitify + +.nyc_output + +*.swp +.DS_Store + dist dist-dev + +.idea diff --git a/.madrun.js b/.madrun.mjs similarity index 69% rename from .madrun.js rename to .madrun.mjs index efa8ce98..c8779b5f 100644 --- a/.madrun.js +++ b/.madrun.mjs @@ -1,45 +1,47 @@ +import process from 'node:process'; import {run, cutEnv} from 'madrun'; -import {defineEnv} from 'supertape/env'; -const testEnv = defineEnv({ - timeout: 7000, - css: true, -}); +const testEnv = { + SUPERTAPE_TIMEOUT: 7000, +}; -const buildEnv = { +const is17 = /^v1[789]/.test(process.version); +const is20 = process.version.startsWith('v2'); + +// fix for ERR_OSSL_EVP_UNSUPPORTED on node v17 +// flag '--openssl-legacy-provider' not supported +// on earlier version of node.js +// +// https://stackoverflow.com/a/69746937/4536327 +const buildEnv = (is17 || is20) && { + NODE_OPTIONS: '--openssl-legacy-provider', NODE_ENV: 'production', }; export default { - 'start': () => 'node bin/cloudcmd.js', + 'start': () => 'node bin/cloudcmd.mjs', 'start:dev': async () => await run('start', null, { NODE_ENV: 'development', }), 'build:start': () => run(['build:client', 'start']), - 'build:start:dev': () => run([ - 'build:client:dev', - 'start:dev', - ]), + 'build:start:dev': () => run(['build:client:dev', 'start:dev']), 'lint:all': () => run('lint:progress'), - 'lint': () => 'redlint fix; putout .', + 'lint': () => 'putout .', 'lint:progress': () => run('lint', '-f progress'), 'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"', 'fresh:lint': () => run('lint', '--fresh'), 'lint:fresh': () => run('lint', '--fresh'), - 'fix:lint': async () => `putout --fix . && redlint fix`, + 'fix:lint': () => run('lint', '--fix'), 'lint:stream': () => run('lint', '-f stream'), - 'test': () => [testEnv, `tape '{test}/**/*.js' '{bin,client,static,common,server}/**/*.spec.js' -f fail`], - 'test:e2e': () => `tape 'test-e2e/**/*.js'`, + 'test': () => [testEnv, `tape 'test/**/*.{js,mjs}' '{client,static,common,server}/**/*.spec.{js,mjs}' -f fail`], 'test:client': () => `tape 'test/client/**/*.js'`, 'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`, - 'wisdom': async () => await run(['lint:all', 'build', 'test'], null, { - CI: 1, - }), - 'wisdom:type': () => 'bin/release.js', + 'wisdom': () => run(['lint:all', 'build', 'test']), + 'wisdom:type': () => 'bin/release.mjs', 'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`], 'coverage:report': () => 'c8 report --reporter=lcov', 'report': () => 'c8 report --reporter=lcov', - '6to5': () => [buildEnv, 'rspack build --config rspack.config.js'], + '6to5': () => [buildEnv, 'webpack --progress'], '6to5:client': () => run('6to5', '--mode production'), '6to5:client:dev': async () => await run('6to5', '--mode development', { NODE_ENV: 'development', @@ -53,7 +55,6 @@ export default { 'watch:test:client': async () => `nodemon -w client -w test/client -x ${await run('test:client')}`, 'watch:test:server': async () => `nodemon -w client -w test/client -x ${await run('test:server')}`, 'watch:coverage': async () => [testEnv, `nodemon -w server -w test -w common -x ${await cutEnv('coverage')}`], - 'watch:fix:lint': async () => `nodemon -w client -w server -w test -w common -x '${await run('fix:lint')}'`, 'build': async () => run('6to5:*'), 'build:dev': async () => run('build:client:dev'), 'build:client': () => run('6to5:client'), diff --git a/.npmignore b/.npmignore index b83b5923..b668cf7f 100644 --- a/.npmignore +++ b/.npmignore @@ -1,29 +1,34 @@ +.* *.spec.* *.config.* *.fixture.js* -*.ai -*.cdr -*.eps -*.log -*.lock - -.* - manifest.yml -docker-compose.yml -now.json -app.json -bower.json -deno.json -bin/release.* -img/logo/cloudcmd-hq.png -webpack.config.js - docker -test* +docker-compose.yml +test fixture fixture-* coverage css html +yarn-error.log +yarn.lock +now.json + +app.json +bower.json +manifest.yml +deno.json + +bin/release.mjs + client +img/logo/cloudcmd-hq.png + +webpack.config.js + +*.ai +*.cdr +*.eps + +*.config.* diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..1efe0ac6 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20.15.1 diff --git a/.nycrc.json b/.nycrc.json index 1fe174f3..2a772159 100644 --- a/.nycrc.json +++ b/.nycrc.json @@ -1,11 +1,11 @@ { - "checkCoverage": false, + "check-coverage": false, "all": false, "exclude": [ "**/*.spec.js", + "**/fixture", "**/*.*.js", "**/*.config.*", - "**/fixture", "**/test/**" ], "branches": 100, diff --git a/.putout.json b/.putout.json index 7154775a..800cdeab 100644 --- a/.putout.json +++ b/.putout.json @@ -1,11 +1,11 @@ { "plugins": ["cloudcmd"], "ignore": [ - "*.md", + "html", + "fixture*", "app.json", "fontello.json", - "html", - "fixture*" + "*.md" ], "rules": { "package-json/add-type": "off" diff --git a/.typos.toml b/.typos.toml index dc8af253..bb698401 100644 --- a/.typos.toml +++ b/.typos.toml @@ -1,2 +1,2 @@ [files] -extend-exclude = ["ChangeLog"] +extend-exclude= ["ChangeLog", "*.js"] diff --git a/.rspack/css.js b/.webpack/css.mjs similarity index 52% rename from .rspack/css.js rename to .webpack/css.mjs index 10d28250..338ae91b 100644 --- a/.rspack/css.js +++ b/.webpack/css.mjs @@ -1,18 +1,21 @@ import {env} from 'node:process'; -import {rspack} from '@rspack/core'; +import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; -const {CssExtractRspackPlugin} = rspack; const isDev = env.NODE_ENV === 'development'; -const plugins = [ - new CssExtractRspackPlugin({ +const clean = (a) => a.filter(Boolean); + +const plugins = clean([ + new MiniCssExtractPlugin({ filename: '[name].css', }), -]; + !isDev && new OptimizeCssAssetsPlugin(), +]); const rules = [{ test: /\.css$/i, - use: [CssExtractRspackPlugin.loader, { + use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: true, @@ -24,15 +27,8 @@ const rules = [{ }]; export default { - mode: isDev ? 'development' : 'production', plugins, module: { rules, }, - optimization: { - minimize: !isDev, - minimizer: [ - new rspack.LightningCssMinimizerRspackPlugin(), - ], - }, }; diff --git a/.rspack/html.js b/.webpack/html.mjs similarity index 100% rename from .rspack/html.js rename to .webpack/html.mjs diff --git a/.rspack/js.js b/.webpack/js.mjs similarity index 74% rename from .rspack/js.js rename to .webpack/js.mjs index 251d85f6..38ac6b8d 100644 --- a/.rspack/js.js +++ b/.webpack/js.mjs @@ -1,15 +1,12 @@ import {resolve, sep} from 'node:path'; -import {fileURLToPath} from 'node:url'; import {env} from 'node:process'; -import {rspack} from '@rspack/core'; - -const resolveModule = (a) => fileURLToPath(import.meta.resolve(a)); +import webpack from 'webpack'; +import WebpackBar from 'webpackbar'; const { EnvironmentPlugin, NormalModuleReplacementPlugin, - ProvidePlugin, -} = rspack; +} = webpack; const modules = './modules'; const dirModules = './client/modules'; @@ -25,31 +22,27 @@ const dist = resolve(rootDir, 'dist'); const distDev = resolve(rootDir, 'dist-dev'); const devtool = isDev ? 'eval' : 'source-map'; -const noParse = (a) => a.endsWith('.spec.js'); +const notEmpty = (a) => a; +const clean = (array) => array.filter(notEmpty); -// codegen.macro is a babel-macro (build-time codegen), not supported by -// Rspack's native SWC transform, so client/sw/sw.js (the only file that -// uses it) keeps going through babel-loader. Everything else uses -// Rspack's built-in SWC loader, which is the main source of the speedup. -const rules = [{ - test: /sw\/sw\.js$/, - exclude: /node_modules/, - loader: 'babel-loader', -}, { - test: /\.[mc]?js$/, - exclude: [/node_modules/, /sw\/sw\.js$/], - loader: 'builtin:swc-loader', - options: { - jsc: { - parser: { - syntax: 'ecmascript', - }, - }, - env: { - targets: 'defaults', - }, +const noParse = (a) => a.endsWith('.spec.js'); +const options = { + babelrc: true, +}; + +const rules = clean([ + !isDev && { + test: /\.[mc]?js$/, + exclude: /node_modules/, + loader: 'babel-loader', }, -}]; + isDev && { + test: /\.[mc]?js$/, + exclude: /node_modules/, + loader: 'babel-loader', + options, + }, +]); const plugins = [ new NormalModuleReplacementPlugin(/^node:/, (resource) => { @@ -59,9 +52,7 @@ const plugins = [ new EnvironmentPlugin({ NODE_ENV, }), - new ProvidePlugin({ - process: 'process/browser', - }), + new WebpackBar(), ]; const splitChunks = { @@ -83,8 +74,6 @@ const splitChunks = { 'themes/light', 'columns/name-size', 'columns/name-size-date', - 'columns/name-size-time', - 'columns/name-size-date-time', ]; return !lazyChunks.includes(chunk.name); @@ -105,9 +94,8 @@ export default { 'node:path': 'path', }, fallback: { - path: resolveModule('path-browserify'), - process: resolveModule('process/browser'), - util: resolveModule('util'), + path: import.meta.resolve('path-browserify'), + process: import.meta.resolve('process/browser'), }, }, devtool, @@ -119,15 +107,14 @@ export default { 'themes/light': `${dirThemes}/light.css`, 'columns/name-size': `${dirColumns}/name-size.css`, 'columns/name-size-date': `${dirColumns}/name-size-date.css`, - 'columns/name-size-date-time': `${dirColumns}/name-size-date-time.css`, 'nojs': `${dirCss}/nojs.css`, 'help': `${dirCss}/help.css`, 'view': `${dirCss}/view.css`, 'config': `${dirCss}/config.css`, 'terminal': `${dirCss}/terminal.css`, 'user-menu': `${dirCss}/user-menu.css`, - 'sw': `${dir}/sw/sw.js`, - 'cloudcmd': `${dir}/cloudcmd.js`, + 'sw': `${dir}/sw/sw.mjs`, + 'cloudcmd': `${dir}/cloudcmd.mjs`, [`${modules}/edit`]: `${dirModules}/edit.js`, [`${modules}/edit-file`]: `${dirModules}/edit-file.js`, [`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`, @@ -137,10 +124,10 @@ export default { [`${modules}/view`]: `${dirModules}/view/index.js`, [`${modules}/help`]: `${dirModules}/help.js`, [`${modules}/markdown`]: `${dirModules}/markdown.js`, - [`${modules}/config`]: `${dirModules}/config/index.js`, + [`${modules}/config`]: `${dirModules}/config/index.mjs`, [`${modules}/contact`]: `${dirModules}/contact.js`, [`${modules}/upload`]: `${dirModules}/upload.js`, - [`${modules}/operation`]: `${dirModules}/operation/index.js`, + [`${modules}/operation`]: `${dirModules}/operation/index.mjs`, [`${modules}/konsole`]: `${dirModules}/konsole.js`, [`${modules}/terminal`]: `${dirModules}/terminal.js`, [`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`, @@ -162,7 +149,7 @@ export default { }, plugins, performance: { - maxEntrypointSize: 800_000, + maxEntrypointSize: 600_000, maxAssetSize: 600_000, }, }; diff --git a/ChangeLog b/ChangeLog index c5e0fbca..0d1f6662 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,686 +1,3 @@ -2026.06.15, v19.19.1 - -feature: -- 78b206e0 client: key: Ctrl + L -> Ctrl + Shift + L: Log out (#466) -- b1492885 docker: io: NPM_CONFIG_PACKAGE_LOCK=false -- 76d7bf4d docker: typos, shellcheck: add - -2026.05.26, v19.19.0 - -feature: -- 4ee38db6 client: view: flac: add support - -2026.05.26, v19.18.1 - -feature: -- 40ecef5e cloudcmd: ratelimit: X-Forwarded-For (#437) - -2026.05.26, v19.18.0 - -fix: -- 161bede8 client: menu: @putout/bundle@5.5. -- 447d990f cloudcmd: server: rest: sendError - -feature: -- 5d9628ce cloudcmd: rate limit: add support (#437) -- 2be395e6 cloudcmd: get rid of manifest: 401 basic auth - -2026.05.17, v19.17.0 - -fix: -- d96f6c46 css: query: hide time on mobile -- e7c55e05 docker: io: XDG_CONFIG_HOME - -feature: -- ae1ca0f8 cloudcmd: cssnano-preset-default v8.0.1 -- 94d5096a style: owner, mode: improve -- 807f8346 cloudcmd: superc8 v12.6.0 -- 67a95722 docker: io: cline: add -- 68eacb91 qword: add -- 4acd294b docker: io: tmux -- 05c80043 cloudcmd: @supertape/loader-css v1.0.0 -- 800a6545 cloudcmd: eslint-plugin-n v18.0.1 -- a985bb36 cloudcmd: supertape v13.0.0 -- 4315ec61 docker: io XDG_CONFIG_HOME: /etc -> /usr/local/etc - -2026.05.03, v19.16.0 - -feature: -- d82d0335 client: vim: rr for rename file -- acfa27cf docker: io: nvchad: add -- 14f009f8 docker: io: bash-completion: add -- c6c60146 docker: io: f4 -- 9a6b8935 docker: io: GOPATH -- a9bc22ce cloudcmd: operation: rm useless checks -- 36bacfe9 cloudcmd: client: key: vim: cc, mm -- 591da25c cloudcmd: ponse v8.0.0 -- 1e1b073c docker: io: /usr/src/cloudcmd -> /usr/local/share/cloudcmd - -2026.04.28, v19.15.0 - -feature: -- 9d97343b cloudcmd: operation: rm useless checks -- 46a88cfa cloudcmd: client: key: vim: cc, mm - -2026.04.28, v19.14.0 - -feature: -- 36a8b641 cloudcmd: ponse v8.0.0 -- 1c263c18 docker: io: /usr/src/cloudcmd -> /usr/local/share/cloudcmd -- ea7b828c docker: io: ubuntu: resolute -- cd6c11ac docker: io: far2l -- c4beeec6 docker: io: gdu: add - -2026.04.21, v19.13.1 - -feature: -- d5cd11e8 cloudcmd: montag v2.0.1 -- dc94d2db docker: io: add latest git version -- 1637beee docker: io: /usr/local/src -> /usr/local/share -- b1bc4e73 Docker: io: pv -- 7c0dca60 docker: io: git: master - -2026.04.15, v19.13.0 - -fix: -- 48693d9e docker: io: XDG_CACHE_HOME - -feature: -- 4b2395c5 cloudcmd: Ctrl + L: logout -- 977a8aaa docker: io: strace: add -- 7d0098fd docker: io: XDG_CACHE_HOME=/tmp/cache -- a89e901b actions: docker: io: add - -2026.04.12, v19.12.5 - -feature: -- 1cfc1a6f docker: io: only amd64 - -2026.04.12, v19.12.4 - -feature: -- 3c2b5658 docker: io: haskell -- d37f8cd3 docker: io: palabra: node - -2026.04.12, v19.12.3 - -feature: -- cb6cabd4 docker: io: PALABRA_DIR - -2026.04.11, v19.12.2 - -feature: -- 2ce11fa2 docker: io: get rid of haskell: to slow install -- aa741232 docker: io: rizin, yara - -2026.04.09, v19.12.1 - -feature: -- 5bd03215 docker: io: add PREFIX - -2026.04.09, v19.12.0 - -feature: -- 13b15b7b docker: io: palabra - -2026.04.07, v19.11.14 - -feature: -- a31beab0 docker: io: rustup env - -2026.04.07, v19.11.13 - -feature: -- 6a08479a docker: io: BUN_INSTALL - -2026.04.06, v19.11.12 - -feature: -- 46c65554 docker: io: npm_config_cache - -2026.04.06, v19.11.11 - -feature: -- ae2ce388 docker: io: $DENO_DIR - -2026.04.06, v19.11.10 - -feature: -- 12ea14ac docker: io: nvm: node - -2026.04.06, v19.11.9 - -feature: -- ea96d13f docker: io: net-tools - -2026.04.06, v19.11.8 - -feature: -- 5c08565f docker: io: debian -> ubuntu - -2026.04.05, v19.11.7 - -feature: -- 36cdef37 docker: io: bookworm - -2026.04.05, v19.11.6 - -feature: -- 845f9bd1 cloudcmd: gritty v10.2.0 - -2026.04.05, v19.11.5 - -feature: -- f9c513cc docker: io: hexyl: add - -2026.04.04, v19.11.4 - -feature: -- e7347d25 docker: io: neovim: apt -> github - -2026.04.04, v19.11.3 - -feature: -- 9970ff76 docker: io: btop -- 4395a471 docker: io: $PATH: add $HOME/.local/bin - -2026.04.04, v19.11.2 - -feature: -- c40ae8e4 docker: io: htop -- f463c5c5 docker: io: aptitude: add -- d1032f09 docker: io: remove unused - -2026.04.04, v19.11.1 - -feature: -- 7787bfc2 cloudcmd: user-menu: runFromCDN - -2026.04.04, v19.11.0 - -feature: -- 32f89d38 cloudcmd: user-menu: root - -2026.04.04, v19.10.2 - -feature: -- df4fb517 cloudcmd: aleman v2.0.1 - -2026.04.03, v19.10.1 - -feature: -- ceb7ef4f docker: io: keep /var/lib/apt/lists - -2026.04.02, v19.10.0 - -fix: -- d12e7bd0 distribute: fix event listener leak on socket disconnect (#462) - -2026.04.02, v19.9.24 - -feature: -- c4d26c6a docker: io: apt-get upgrade -- 9ddb8c29 docker: io: get rid of nix -- e4d7d441 docker: io: nix - -2026.04.01, v19.9.23 - -feature: -- 5a3413ce docker: io: nix - -2026.03.31, v19.9.22 - -feature: -- b4345ed4 docker: io: DENO_DIR - -2026.03.31, v19.9.21 - -fix: -- 5c6a9a95 css: columns: name: 40% -> 35% - -2026.03.30, v19.9.20 - -feature: -- cd0b5554 iocmd: io: nvm - -2026.03.30, v19.9.19 - -feature: -- 6a52b11e docker: io: go, rust - -2026.03.30, v19.9.18 - -feature: -- 738059f2 docker: io: fzf -- 3fc8932f docker: io: less, el_GR - -2026.03.30, v19.9.17 - -feature: -- cf424d6c docker: io: command-not-found update - -2026.03.29, v19.9.16 - -feature: -- 19347a2b docker: io: add command-not-found - -2026.03.29, v19.9.15 - -feature: -- ee170552 docker: io: ubuntu -- e04c4594 docker: io: net-tools: add - -2026.03.29, v19.9.14 - -fix: -- 6c7709be docker: io: PS1 - -2026.03.29, v19.9.13 - -fix: -- 439b3710 bin: currify -- e4182841 docker: io: xterm-256color - -2026.03.29, v19.9.12 - -feature: -- 811a47fd cloudcmd: bin: get rid of require - -2026.03.29, v19.9.11 - -feature: -- 1f95f188 cloudcmd: get rid of simport - -2026.03.29, v19.9.10 - -feature: -- f0deb323 docker: io: add ja_JP.UTF-8 - -2026.03.29, v19.9.9 - -fix: -- f671f798 docker: io: PS1 environment variable in Dockerfile - -feature: -- cc7f9dc7 docker: io: use ubuntu - -2026.03.28, v19.9.8 - -feature: -- 4d1cd8cd docker: io: ffmpeg -- 54b56fdc cloudcmd: vim: ESC: use only to enable, do not use to disable - -2026.03.27, v19.9.7 - -feature: -- 110908e2 docker: io: apt-get - -2026.03.26, v19.9.6 - -feature: -- 6450a2f8 docker: io: add UTF-8 - -2026.03.26, v19.9.5 - -fix: -- e761cacb columns: name-size-date-time: 20% -> 19% - -2026.03.26, v19.9.4 - -feature: -- 69498ed6 docker: io: pull.rebase by default - -2026.03.24, v19.9.3 - -feature: -- 8763788b docker: io: healthcheck - -2026.03.23, v19.9.2 - -feature: -- 09a02074 docker: io: git config: add -- c448eaa4 docker: io: buni - -2026.03.23, v19.9.1 - -fix: -- 6e5318fa client: modules: config: input: quote - -2026.03.23, v19.9.0 - -feature: -- a1216cdd cloudcmd: add ability to hide port configuration - -2026.03.23, v19.8.15 - -feature: -- 68b2aa78 docker: io: cloudcmd_vim - -2026.03.23, v19.8.14 - -fix: -- 665ed9c2 docker: io: get back port - -2026.03.23, v19.8.13 - -feature: -- 618a5615 docker: io: PS1 - -2026.03.23, v19.8.12 - -feature: -- 556b0150 docker: io: PS1 - -2026.03.23, v19.8.11 - -fix: -- 97672ef5 docker: io: apt-get install -> apt-get - -feature: -- 67f27d84 docker: io: bun, deno - -2026.03.23, v19.8.10 - -feature: -- 025b005e docker: io: add PS1 - -2026.03.23, v19.8.9 - -fix: -- b052cf22 cloudcmd: no time available: --.--.---- -> --:--:-- (#461) - -2026.03.23, v19.8.8 - -feature: -- 02dbe56d server: user-menu: when error send it - -2026.03.23, v19.8.7 - -feature: -- ecc76e8b docker: io: renamify-cli, runny, redfork - -2026.03.23, v19.8.6 - -fix: -- 53c072ab @putout/plugin-cloudcmd: devDependencies -> dependnecies - -feature: -- 4b9922bf docker: /usr/src: app -> cloudcmd - -2026.03.23, v19.8.5 - -fix: -- 56fc8b83 docker: gritty - -2026.03.22, v19.8.4 - -feature: -- 5000227e docker: vim nvim - -2026.03.22, v19.8.3 - -fix: -- 01677e6a docker: io: slim -> io - -feature: -- 7e35c606 docker: io: curl wget - -2026.03.22, v19.8.2 - -feature: -- e5b221f7 docker: io: add - -2026.03.22, v19.8.1 - -feature: -- 708a4c6b docker: slim: add -- 80613f46 docker: slim: add - -2026.03.20, v19.8.0 - -fix: -- 59037f2c cloudcmd: bin: --show-config - -feature: -- 10934b3a cloudcmd: add ability to show modification time (#230) - -2026.03.18, v19.7.1 - -feature: -- b0c1d36c cloudcmd: @cloudcmd/fileop v9.0.7 (#460) - -2026.03.17, v19.7.0 - -feature: -- daf83875 cloudfunc: override date format (#459) - -2026.03.17, v19.6.9 - -feature: -- b28a070a cloudcmd: redzip v4.6.1 -- 43c5a011 cloudcmd: css-minimizer-webpack-plugin v8.0.0 -- 15dcae5c cloudcmd: webpack-cli v7.0.2 -- 5976da81 cloudcmd: @cloudcmd/fileop v9.0.5 -- 37cb83f2 cloudcmd: redzip v4.6.0 - -2026.02.27, v19.6.8 - -feature: -- 15fab514 cloudcmd: copymitter v10.3.0 (#458) - -2026.02.26, v19.6.7 - -feature: -- 68c7d0be cloudcmd: onezip v7.0.0 - -2026.02.26, v19.6.6 - -feature: -- 3987cc82 cloudcmd: redzip v4.5.1 (#457) - -2026.02.26, v19.6.5 - -feature: -- 964ae989 cloudcmd: redzip v4.5.0 (#457) - -2026.02.25, v19.6.4 - -feature: -- a66eeda3 cloudcmd: copymitter v10.2.0 (coderaiser/cloudcmd#457) -- 4340533a cloudcmd: c8 v11.0.0 -- 0857711f cloudcmd: redzip v4.2.0 (#457) - -2026.02.24, v19.6.3 - -feature: -- 2234f1b4 cloudcmd: redzip v4.2.0 (#475) - -2026.02.24, v19.6.2 - -feature: -- 321a54dd cloudcmd: @cloudcmd/fileop v9.0.2 (#457) - -2026.02.24, v19.6.1 - -feature: -- 7c5ac408 cloudcmd: @cloudcmd/fileop v9.0.1 (#457) - -2026.02.21, v19.6.0 - -feature: -- 6d19bf2e common: object.omit -> omit - -2026.02.18, v19.5.1 - -feature: -- 6e1cf4ef cloudcmd: supermenu v5.0.0 - -2026.02.18, v19.5.0 - -feature: -- b20539ef common: entity: encode {,} -- 7ef134f4 cloudcmd: rendy v5.0.0 - -2026.02.18, v19.4.1 - -feature: -- 1e18d513 cloudcmd: @cloudcmd/fileop v9.0.0 - -2026.02.18, v19.4.0 - -fix: -- 45cf9baf menu: prefix (#456) - -feature: -- 3e647290 cloudcmd: redlint v6.0.0 -- 800ed012 cloudcmd: putout v42.0.5 -- 525c17d4 cloudcmd: madrun v13.0.0 -- 44247499 cloudcmd: eslint-plugin-putout v31.0.0 - -2026.02.15, v19.3.9 - -feature: -- 9ffe3ef1 cloudcmd: copymitter v10.0.0 (#457) - -2026.02.15, v19.3.8 - -fix: -- d274a2b3 spinner (#456) - -2026.02.13, v19.3.7 - -feature: -- 8fd79a27 cloudcmd: win32 v8.0.0 - -2026.02.12, v19.3.6 - -feature: -- ac94eccd cloudcmd: konsole: named -- 144e4a34 cloudcmd: gritty v10.0.0 -- 938f9e76 cloudcmd: console-io v15.0.1 - -2026.02.08, v19.3.5 - -feature: -- fb40bd9c rm cssnano: has no sense for spinner, option disabled by default (https://svgo.dev/docs/plugins/convertPathData/) -- 76125be9 cloudcmd: eslint v10.0.0 - -2026.02.06, v19.3.4 - -feature: -- 66a08c7f cloudcmd: deepword v11.0.0 - -2026.02.05, v19.3.3 - -feature: -- 4a5a56f4 cloudcmd: dword v16.0.0 - -2026.02.04, v19.3.2 - -fix: -- 99d8435e cloudcmd: exports -- a266c145 cloudcmd: default -> named -- 6e3ba271 Closing X in editor disappeared (#455) - -feature: -- 30f42e94 cloudcmd: restafary v13.0.1 -- f84ce853 cloudcmd: edward v16.0.0 - -2026.02.03, v19.3.1 - -fix: -- 5661bc4f Closing X in editor disappeared (#455) - -2026.02.03, v19.3.0 - -feature: -- 4533a25c cloudcmd: migrate to ESM -- e8a81c49 client: dom: events: migrate to ESM -- 071141bc client: terminal-run: migrate to ESM - -2026.02.03, v19.2.0 - -feature: -- bb32f7c4 client: dom: migrate to ESM -- 457c83db client: migrate to ESM -- f8a941bd client: modules: markdown: migrate to ESM -- 9d6cffaf client: polifyll: migrate to ESM -- 5b704d06 client: edit-names: migrate to ESM -- 2c2ca8eb client: edit-file: migrate to ESM -- c9f57c5f client: modules: edit-file-vim: migrate to ESM -- 327ac9de client: modules: help: migrate to ESM -- dc34ee87 cloudcmd: @putout/plugin-cloudcmd v5.0.0 -- 3cd3695b client: modules: edit-names-vim: migrate to ESM -- dfcbfd63 client: modules: terminal: migrate to ESM - -2026.02.02, v19.1.21 - -feature: -- e3ad330e client: konsole: migrate to ESM -- 983fd9af client: edit: migrate to ESM -- ebabad94 common: entity: migrate to ESM -- c3b71653 client: dom: images -> #images -- 8cad7514 common: cloudfunc -> #common/cloudfunc -- 1f174870 client: view: migrate to ESM -- 7173f6cb cloudcmd: smalltalk v5.0.0 - -2026.01.31, v19.1.20 - -feature: -- c1014c9c client: dom: operations: rename-current: migrate to ESM -- 2e486f8b cloudcmd: restafary v13.0.0 -- 6addb29f cloudcmd: redzip v4.0.0 - -2026.01.30, v19.1.19 - -feature: -- 2a0feac7 cloudcmd: @cloudcmd/modal v4.0.0 - -2026.01.30, v19.1.18 - -feature: -- 73fa4961 client: modules: user-menu: migrate to ESM -- ebfdf8c0 client: modules: operation: migrate to ESM -- ad683171 client: modules: menu: migrate to ESM -- 0054cfa3 client: dom: load-remote: migrate to ESM -- e4d0ece0 client: dom: rest: migrate to ESM -- c704ffe4 client: dom: storage: migrate to ESM -- 5182cb81 client: modules: upload: migrate to ESM -- 9543f1ad client: dom: upload-files: migrate to ESM - -2026.01.29, v19.1.17 - -feature: -- f9c28319 client: dom: migrate to ESM -- 9d2c4e4a client: dom: cmd: move out -- 5a2aa70f client: dom: simplify require -- dee50a85 client: dom: files: migrate to ESM -- 23c0d770 client: dom: dom-tree: migrate to ESM -- cf2c6415 client: directory: migrate to ESM -- 0eb802e7 client: dom: dialog: migrate to ESM - -2026.01.28, v19.1.16 - -fix: -- 4c242631 css: spinner: do not minify svg - -2026.01.28, v19.1.15 - -feature: -- 265c0b49 client: key: vim: migrate to ESM -- 3bc49f02 client: set-current-by-char: migrate to ESM - -2026.01.28, v19.1.14 - -feature: -- f8a63b5a client: key: binder: migrate to ESM -- 2cc97f33 client: cloud: migrate to ESM - -2026.01.27, v19.1.13 - -feature: -- 41b5a96f client: load: migrate to ESM - 2026.01.27, v19.1.12 feature: @@ -7778,7 +7095,7 @@ with clicks on files. - Removed allowed from cache property in config. - Added ability to hide "Upload To" menu item if -no storage modules set up in modules.json. +no storage modules setted up in modules.json. - From now any file minifying only if last modified time was changed. @@ -7984,7 +7301,7 @@ in any position in anyLoadOnLoad function. - Added chainable to Cache object in DOM. - Changed default ip to null so IP would be geted from -config.json only if it set up. +config.json only if it setted up. - Fixed bug with starting node from other then projects dir. @@ -8293,7 +7610,7 @@ time was possible. - Fixed bug with undefined size on root directory of Cloud Commander. Now Cloud Commander writes size 0, if can't get size, and besides will -set b char: "0b". +setted b char: "0b". - Added supporting of Russian language in directory names. diff --git a/HELP.md b/HELP.md index 2967f0d0..59f41976 100644 --- a/HELP.md +++ b/HELP.md @@ -1,6 +1,6 @@ -# Cloud Commander v19.19.1 +# Cloud Commander v19.1.12 -### [Main][MainURL] [Blog][BlogURL] [Demo][DemoURL] [Deploy](#deploy) +### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] [MainURL]: https://cloudcmd.io "Main" [BlogURL]: https://blog.cloudcmd.io "Blog" @@ -9,17 +9,13 @@ [DWORD]: https://github.com/cloudcmd/dword "Editor based on CodeMirror" [EDWARD]: https://github.com/cloudcmd/edward "Editor based on Ace" [DEEPWORD]: https://github.com/cloudcmd/deepword "Editor based on Monaco" -[QWORD]: https://github.com/cloudcmd/qword "Editor based on CodeMirror 6" [EDWARD_KEYS]: https://github.com/cloudcmd/edward/#hot-keys "Edward Hot keys" [TERMUX]: https://termux.com "Termux" [INLY]: https://github.com/coderaiser/node-inly "Extract archive" -[DeployInstaPodsIMG]: https://img.shields.io/badge/deploy%20on-InstaPods-blue -[DeployInstaPodsURL]: https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd - **Cloud Commander** is a file manager for the web. It includes a command-line console and a text editor. Cloud Commander helps you manage your server and work with files, directories and programs in a web browser from any computer, mobile or tablet. -![Cloud Commander](https://cloudcmd.io/img/logo/cloudcmd.png "Cloud Commander") +![Cloud Commander](/img/logo/cloudcmd.png "Cloud Commander") ## Benefits @@ -30,7 +26,7 @@ - Server works on **Windows**, **Linux**, **Mac OS** and **Android** (with help of [Termux][TERMUX]). - Can be used local or remotely. - Adapts to screen size. -- **3 built-in editors** with support of **syntax highlighting**: [Dword][DWORD], [Edward][EDWARD] [Deepword][DEEPWORD] and [Qword][QWORD]. +- **3 built-in editors** with support of **syntax highlighting**: [Dword][DWORD], [Edward][EDWARD] and [Deepword][DEEPWORD]. - [Console](https://github.com/cloudcmd/console "Console") with support of the default OS command line. - Written in **JavaScript/Node.js**. - Built-in archives pack: **zip** and **tar.gz**. @@ -92,7 +88,6 @@ Cloud Commander supports the following command-line parameters: | `--contact` | enable contact | `--config-dialog` | enable config dialog | `--config-auth` | enable auth change in config dialog -| `--config-port` | enable port change in config dialog | `--console` | enable console | `--sync-console-path` | sync console path | `--terminal` | enable terminal @@ -123,7 +118,6 @@ Cloud Commander supports the following command-line parameters: | `--no-confirm-move` | do not confirm move | `--no-config-dialog` | disable config dialog | `--no-config-auth` | disable auth change in config dialog -| `--no-config-port` | disable port change in config dialog | `--no-console` | disable console | `--no-sync-console-path` | do not sync console path | `--no-contact` | disable contact @@ -195,7 +189,6 @@ Then, start the server again with `cloudcmd` and reload the page. | `Ctrl + F3` | sort by name | `Ctrl + F5` | sort by date | `Ctrl + F6` | sort by size -| `Ctrl + Shift + L` | logout | `Ctrl + Command + .` | show/hide dot files | `Up` | move cursor up | `Down` | move cursor down @@ -218,27 +211,24 @@ Then, start the server again with `cloudcmd` and reload the page. When the `--vim` option is provided, or the configuration parameter `vim` is set, the following hotkeys become available: -| Key |Operation -|:------------|:-------------------------------------------- -| `j` | navigate to next file -| `k` | navigate to previous file -| `dd` | remove current file -| `G` or `$` | navigate to bottom file -| `gg` or `^` | navigate to top file -| `v` | visual mode -| `y` | copy (selected in visual mode files) -| `p` | paste files -| `Esc` | unselect all -| `/` | find file in current directory -| `n` | navigate to next found file -| `N` | navigate to previous found file -| `md` | make directory -| `mf` | make file -| `tt` | show terminal -| `e` | edit file -| `cc` | copy -| `mm` | move -| `rr` | rename file +|Key |Operation +|:----------------------|:-------------------------------------------- +| `j` | navigate to next file +| `k` | navigate to previous file +| `dd` | remove current file +| `G` or `$` | navigate to bottom file +| `gg` or `^` | navigate to top file +| `v` | visual mode +| `y` | copy (selected in visual mode files) +| `p` | paste files +| `Esc` | unselect all +| `/` | find file in current directory +| `n` | navigate to next found file +| `N` | navigate to previous found file +| `md` | make directory +| `mf` | make file +| `tt` | show terminal +| `e` | edit file Commands can be joined, for example: @@ -450,7 +440,6 @@ Some config options can be overridden with environment variables, such as: - `CLOUDCMD_CONTACT` - enable contact - `CLOUDCMD_CONFIG_DIALOG` - enable config dialog - `CLOUDCMD_CONFIG_AUTH` - enable auth change in config dialog -- `CLOUDCMD_CONFIG_PORT` - enable port change in config dialog - `CLOUDCMD_CONSOLE` - enable console - `CLOUDCMD_SYNC_CONSOLE_PATH` - sync console path - `CLOUDCMD_TERMINAL` - enable terminal @@ -699,7 +688,7 @@ And create `index.js`: ```js import http from 'node:http'; -import {cloudcmd} from 'cloudcmd'; +import cloudcmd from 'cloudcmd'; import {Server} from 'socket.io'; import express from 'express'; @@ -933,9 +922,9 @@ ln -s ./sites-available/io.cloudcmd.io ./sites-enabled ## Deploy -`Cloud Commander` can be easily deployed to [InstaPods][DeployInstaPodsURL]. After deploy you receive email with credentials. +`Cloud Commander` can be easily deployed to [Heroku](https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy to Heroku"). -[![Deploy on InstaPods](https://instapods.com/deploy-button.svg)](https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd) +[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.png "Deploy to Heroku")](https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd) ## Docker @@ -1122,117 +1111,6 @@ There are a lot of ways to be involved in `Cloud Commander` development: ## Version history -- *2026.06.15*, **[v19.19.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.19.1)** -- *2026.05.26*, **[v19.19.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.19.0)** -- *2026.05.26*, **[v19.18.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.18.1)** -- *2026.05.26*, **[v19.18.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.18.0)** -- *2026.05.17*, **[v19.17.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.17.0)** -- *2026.05.03*, **[v19.16.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.16.0)** -- *2026.04.28*, **[v19.15.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.15.0)** -- *2026.04.28*, **[v19.14.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.14.0)** -- *2026.04.21*, **[v19.13.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.13.1)** -- *2026.04.15*, **[v19.13.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.13.0)** -- *2026.04.12*, **[v19.12.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.5)** -- *2026.04.12*, **[v19.12.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.4)** -- *2026.04.12*, **[v19.12.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.3)** -- *2026.04.11*, **[v19.12.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.2)** -- *2026.04.09*, **[v19.12.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.1)** -- *2026.04.09*, **[v19.12.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.12.0)** -- *2026.04.07*, **[v19.11.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.14)** -- *2026.04.07*, **[v19.11.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.13)** -- *2026.04.06*, **[v19.11.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.12)** -- *2026.04.06*, **[v19.11.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.11)** -- *2026.04.06*, **[v19.11.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.10)** -- *2026.04.06*, **[v19.11.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.9)** -- *2026.04.06*, **[v19.11.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.8)** -- *2026.04.05*, **[v19.11.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.7)** -- *2026.04.05*, **[v19.11.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.6)** -- *2026.04.05*, **[v19.11.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.5)** -- *2026.04.04*, **[v19.11.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.4)** -- *2026.04.04*, **[v19.11.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.3)** -- *2026.04.04*, **[v19.11.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.2)** -- *2026.04.04*, **[v19.11.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.1)** -- *2026.04.04*, **[v19.11.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.11.0)** -- *2026.04.04*, **[v19.10.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.10.2)** -- *2026.04.03*, **[v19.10.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.10.1)** -- *2026.04.02*, **[v19.10.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.10.0)** -- *2026.04.02*, **[v19.9.24](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.24)** -- *2026.04.01*, **[v19.9.23](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.23)** -- *2026.03.31*, **[v19.9.22](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.22)** -- *2026.03.31*, **[v19.9.21](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.21)** -- *2026.03.30*, **[v19.9.20](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.20)** -- *2026.03.30*, **[v19.9.19](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.19)** -- *2026.03.30*, **[v19.9.18](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.18)** -- *2026.03.30*, **[v19.9.17](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.17)** -- *2026.03.29*, **[v19.9.16](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.16)** -- *2026.03.29*, **[v19.9.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.15)** -- *2026.03.29*, **[v19.9.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.14)** -- *2026.03.29*, **[v19.9.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.13)** -- *2026.03.29*, **[v19.9.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.12)** -- *2026.03.29*, **[v19.9.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.11)** -- *2026.03.29*, **[v19.9.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.10)** -- *2026.03.29*, **[v19.9.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.9)** -- *2026.03.28*, **[v19.9.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.8)** -- *2026.03.27*, **[v19.9.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.7)** -- *2026.03.26*, **[v19.9.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.6)** -- *2026.03.26*, **[v19.9.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.5)** -- *2026.03.26*, **[v19.9.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.4)** -- *2026.03.24*, **[v19.9.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.3)** -- *2026.03.23*, **[v19.9.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.2)** -- *2026.03.23*, **[v19.9.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.1)** -- *2026.03.23*, **[v19.9.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.9.0)** -- *2026.03.23*, **[v19.8.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.15)** -- *2026.03.23*, **[v19.8.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.14)** -- *2026.03.23*, **[v19.8.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.13)** -- *2026.03.23*, **[v19.8.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.12)** -- *2026.03.23*, **[v19.8.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.11)** -- *2026.03.23*, **[v19.8.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.10)** -- *2026.03.23*, **[v19.8.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.9)** -- *2026.03.23*, **[v19.8.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.8)** -- *2026.03.23*, **[v19.8.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.7)** -- *2026.03.23*, **[v19.8.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.6)** -- *2026.03.23*, **[v19.8.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.5)** -- *2026.03.22*, **[v19.8.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.4)** -- *2026.03.22*, **[v19.8.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.3)** -- *2026.03.22*, **[v19.8.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.2)** -- *2026.03.22*, **[v19.8.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.1)** -- *2026.03.20*, **[v19.8.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.8.0)** -- *2026.03.18*, **[v19.7.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.7.1)** -- *2026.03.17*, **[v19.7.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.7.0)** -- *2026.03.17*, **[v19.6.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.9)** -- *2026.02.27*, **[v19.6.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.8)** -- *2026.02.26*, **[v19.6.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.7)** -- *2026.02.26*, **[v19.6.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.6)** -- *2026.02.26*, **[v19.6.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.5)** -- *2026.02.25*, **[v19.6.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.4)** -- *2026.02.24*, **[v19.6.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.3)** -- *2026.02.24*, **[v19.6.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.2)** -- *2026.02.24*, **[v19.6.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.1)** -- *2026.02.21*, **[v19.6.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.6.0)** -- *2026.02.18*, **[v19.5.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.5.1)** -- *2026.02.18*, **[v19.5.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.5.0)** -- *2026.02.18*, **[v19.4.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.4.1)** -- *2026.02.18*, **[v19.4.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.4.0)** -- *2026.02.15*, **[v19.3.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.9)** -- *2026.02.15*, **[v19.3.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.8)** -- *2026.02.13*, **[v19.3.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.7)** -- *2026.02.12*, **[v19.3.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.6)** -- *2026.02.08*, **[v19.3.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.5)** -- *2026.02.06*, **[v19.3.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.4)** -- *2026.02.05*, **[v19.3.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.3)** -- *2026.02.04*, **[v19.3.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.2)** -- *2026.02.03*, **[v19.3.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.1)** -- *2026.02.03*, **[v19.3.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.3.0)** -- *2026.02.03*, **[v19.2.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.2.0)** -- *2026.02.02*, **[v19.1.21](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.21)** -- *2026.01.31*, **[v19.1.20](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.20)** -- *2026.01.30*, **[v19.1.19](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.19)** -- *2026.01.30*, **[v19.1.18](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.18)** -- *2026.01.29*, **[v19.1.17](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.17)** -- *2026.01.28*, **[v19.1.16](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.16)** -- *2026.01.28*, **[v19.1.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.15)** -- *2026.01.28*, **[v19.1.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.14)** -- *2026.01.27*, **[v19.1.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.13)** - *2026.01.27*, **[v19.1.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.12)** - *2026.01.25*, **[v19.1.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.11)** - *2026.01.25*, **[v19.1.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.10)** diff --git a/README.md b/README.md index dfe8d8c6..d7b08179 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cloud Commander v19.19.1 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [![Deploy on InstaPods][DeployInstaPodsIMG]][DeployInstaPodsURL] +# Cloud Commander v19.1.12 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] @@ -15,8 +15,6 @@ [GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg [DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy" [DeployIMG]: https://www.herokucdn.com/deploy/button.png -[DeployInstaPodsIMG]: https://img.shields.io/badge/deploy%20on-InstaPods-blue -[DeployInstaPodsURL]: https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd **Cloud Commander** a file manager for the web with console and editor. @@ -71,7 +69,7 @@ And create `index.js`: ```js import http from 'node:http'; -import {cloudcmd} from 'cloudcmd'; +import cloudcmd from 'cloudcmd'; import {Server} from 'socket.io'; import express from 'express'; diff --git a/bin/cloudcmd.js b/bin/cloudcmd.mjs similarity index 87% rename from bin/cloudcmd.js rename to bin/cloudcmd.mjs index 6ca6716c..e1c358ed 100755 --- a/bin/cloudcmd.js +++ b/bin/cloudcmd.mjs @@ -1,21 +1,24 @@ #!/usr/bin/env node import process from 'node:process'; +import {createRequire} from 'node:module'; import {promisify} from 'node:util'; 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.js'; -import * as env from '../server/env.js'; +import {createConfig, configPath} from '../server/config.mjs'; +import * as env from '../server/env.mjs'; import prefixer from '../server/prefixer.js'; -import * as validate from '../server/validate.js'; -import Info from '../package.json' with { - type: 'json', -}; +import * as validate from '../server/validate.mjs'; process.on('unhandledRejection', exit); +const require = createRequire(import.meta.url); + +const Info = require('../package.json'); const isUndefined = (a) => typeof a === 'undefined'; +const simport = createSimport(import.meta.url); const choose = (a, b) => { if (isUndefined(a)) @@ -28,6 +31,8 @@ const config = createConfig({ configPath, }); +const DIR_SERVER = '../server/'; + const maybeRoot = (a) => { if (a === '.') return process.cwd(); @@ -73,7 +78,6 @@ const yargsOptions = { 'open', 'config-dialog', 'config-auth', - 'config-port', 'console', 'sync-console-path', 'contact', @@ -115,7 +119,7 @@ const yargsOptions = { 'columns': env.parse('columns') || config('columns') || '', 'theme': env.parse('theme') || config('theme') || '', 'vim': choose(env.bool('vim'), config('vim')), - 'log': choose(env.bool('log'), config('log')), + 'log': config('log'), 'import-url': env.parse('import_url') || config('importUrl'), 'import-listen': choose(env.bool('import_listen'), config('importListen')), @@ -128,7 +132,6 @@ 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')), - 'config-port': choose(env.bool('config_port'), config('configPort')), 'terminal-path': env.parse('terminal_path') || config('terminalPath'), 'terminal-command': env.parse('terminal_command') || config('terminalCommand'), 'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')), @@ -165,7 +168,7 @@ else main(); async function main() { - const {validateArgs} = await import('@putout/cli-validate-args'); + const {validateArgs} = await simport('@putout/cli-validate-args'); const error = await validateArgs(args, [ ...yargsOptions.boolean, @@ -176,7 +179,7 @@ async function main() { return exit(error); if (args.repl) - await repl(); + repl(); validate.columns(args.columns); validate.theme(args.theme); @@ -211,7 +214,6 @@ async function main() { config('oneFilePanel', args.oneFilePanel); config('configDialog', args.configDialog); config('configAuth', args.configAuth); - config('configPort', args.configPort); config('keysPanel', args.keysPanel); config('export', args.export); config('exportToken', args.exportToken); @@ -246,7 +248,7 @@ async function main() { if (args.showConfig) await showConfig(); - const {distributeImport} = await import('../server/distribute/import.js'); + const {distributeImport} = await simport('../server/distribute/import.mjs'); const importConfig = promisify(distributeImport); await start(options, config); @@ -264,12 +266,11 @@ function validateRoot(root, config) { if (root === '/') return; - if (config('log')) - console.log(`root: ${root}`); + console.log(`root: ${root}`); } async function getPassword(password) { - const {default: criton} = await import('criton'); + const criton = await simport('criton'); return criton(password, config('algo')); } @@ -278,10 +279,12 @@ function version() { } async function start(options, config) { + const SERVER = `${DIR_SERVER}server.mjs`; + if (!args.server) return; - const {default: server} = await import('../server/server.js'); + const server = await simport(SERVER); server(options, config); } @@ -295,8 +298,8 @@ function port(arg) { } async function showConfig() { - const {showConfig} = await import('../server/show-config.js'); - const data = showConfig(config('*')); + const show = await simport('../server/show-config'); + const data = show(config('*')); console.log(data); } @@ -305,13 +308,13 @@ async function readConfig(name) { if (!name) return; - const {default: forEachKey} = await import('for-each-key'); + const tryToCatch = await simport('try-to-catch'); + const forEachKey = await simport('for-each-key'); - const data = await import(name, { - with: { - type: 'json', - }, - }); + const [error, data] = await tryToCatch(simport, name); + + if (error) + return exit(error.message); forEachKey(config, data); } @@ -323,8 +326,8 @@ async function help() { }, }); - const {default: forEachKey} = await import('for-each-key'); - const {default: currify} = await import('currify'); + const forEachKey = await simport('for-each-key'); + const currify = await simport('currify'); const usage = 'Usage: cloudcmd [options]'; const url = Info.homepage; @@ -336,15 +339,15 @@ async function help() { console.log('\nGeneral help using Cloud Commander: <%s>', url); } -async function repl() { +function repl() { console.log('REPL mode enabled (telnet localhost 1337)'); - await import('../server/repl.js'); + require(`${DIR_SERVER}repl`); } async function checkUpdate() { - const {default: load} = await import('package-json'); - const {version} = await load(Info.name, 'latest'); + const load = await simport('package-json'); + const {version} = await load(Info.name, 'latest'); await showUpdateInfo(version); } @@ -352,7 +355,7 @@ async function showUpdateInfo(version) { if (version === Info.version) return; - const {default: chalk} = await import('chalk'); + const chalk = await simport('chalk'); const latestVersion = chalk.green.bold(`v${version}`); const latest = `update available: ${latestVersion}`; diff --git a/bin/cloudcmd.spec.js b/bin/cloudcmd.spec.js deleted file mode 100644 index d09541b9..00000000 --- a/bin/cloudcmd.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -import {spawnSync} from 'node:child_process'; -import {test} from 'supertape'; -import info from '../package.json' with { - type: 'json', -}; - -const cliPath = new URL('cloudcmd.js', import.meta.url).pathname; - -test('cloudcmd: bin: cli: -h', (t) => { - const {stdout} = spawnSync(cliPath, ['-h'], { - encoding: 'utf8', - }); - - t.match(stdout, `Options`); - t.end(); -}); - -test('cloudcmd: bin: cli: -v', (t) => { - const {version} = info; - const {stdout} = spawnSync(cliPath, ['-v'], { - encoding: 'utf8', - }); - - t.match(stdout, `v${version}`); - t.end(); -}); diff --git a/bin/release.js b/bin/release.mjs similarity index 84% rename from bin/release.js rename to bin/release.mjs index 86e1d714..004b908e 100755 --- a/bin/release.js +++ b/bin/release.mjs @@ -3,27 +3,26 @@ import {promisify} from 'node:util'; import process from 'node:process'; import {tryToCatch} from 'try-to-catch'; +import {createSimport} from 'simport'; import minor from 'minor'; import _place from 'place'; -import {rendy} from 'rendy'; +import rendy from 'rendy'; import shortdate from 'shortdate'; -import Info from '../package.json' with { - type: 'json', -}; +const simport = createSimport(import.meta.url); const place = promisify(_place); +const Info = await simport('../package.json'); + await main(); async function main() { const history = '## Version history\n\n'; const link = '//github.com/coderaiser/cloudcmd/releases/tag/'; - const template = '- ' + '*{{ date }}*, ' + '**[v{{ version }}]' + - '(' + - link + + '(' + link + 'v{{ version }})**\n'; const {version} = Info; @@ -36,11 +35,10 @@ async function main() { await replaceVersion('README.md', version, versionNew); await replaceVersion('HELP.md', version, versionNew); - const historyNew = history + - rendy(template, { - date: shortdate(), - version: versionNew, - }); + const historyNew = history + rendy(template, { + date: shortdate(), + version: versionNew, + }); await replaceVersion('HELP.md', history, historyNew); } diff --git a/client/client.js b/client/client.mjs similarity index 96% rename from client/client.js rename to client/client.mjs index a0e3cb07..b2f55b36 100644 --- a/client/client.js +++ b/client/client.mjs @@ -1,22 +1,24 @@ import process from 'node:process'; + +/* global DOM */ import Emitify from 'emitify'; import inherits from 'inherits'; -import {rendy} from 'rendy'; +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.mjs'; +import {getJsonFromFileTable} from './get-json-from-file-table.mjs'; +import {Key} from './key/index.mjs'; import { apiURL, formatMsg, buildFromJSON, -} from '#common/cloudfunc'; -import * as Images from '#dom/images'; -import {unregisterSW} from './sw/register.js'; -import {getJsonFromFileTable} from './get-json-from-file-table.js'; -import {Key} from './key/index.js'; -import {loadModule} from './load-module.js'; +} from '../common/cloudfunc.mjs'; +import {loadModule} from './load-module.mjs'; const noJS = (a) => a.replace(/.js$/, ''); @@ -348,12 +350,7 @@ function CloudCmdProto({DOM, Listeners}) { 'pathLink', ]; - const [error, [ - file, - path, - link, - pathLink, - ]] = await tryToCatch(Files.get, names); + const [error, [file, path, link, pathLink]] = await tryToCatch(Files.get, names); if (error) return DOM.Dialog.alert(error.responseText); diff --git a/client/cloudcmd.js b/client/cloudcmd.mjs similarity index 79% rename from client/cloudcmd.js rename to client/cloudcmd.mjs index f2416fd4..4174466b 100644 --- a/client/cloudcmd.js +++ b/client/cloudcmd.mjs @@ -1,14 +1,14 @@ -import '../css/main.css'; import process from 'node:process'; import wraptile from 'wraptile'; import load from 'load.js'; -import * as Util from '#common/util'; -import * as CloudFunc from '#common/cloudfunc'; -import DOM from '#dom'; -import {registerSW, listenSW} from './sw/register.js'; -import {initSortPanel, sortPanel} from './sort.js'; -import {createCloudCmd} from './client.js'; -import * as Listeners from './listeners/index.js'; +import '../css/main.css'; +import {registerSW, listenSW} from './sw/register.mjs'; +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.mjs'; const isDev = process.env.NODE_ENV === 'development'; @@ -36,7 +36,7 @@ async function init(config) { if (globalThis.CloudCmd.config('menu') === 'aleman') setTimeout(() => { import('https://esm.sh/@putout/processor-html'); - import('https://esm.sh/@putout/bundle@5.5'); + import('https://esm.sh/@putout/bundle'); }, 100); } diff --git a/client/dom/buffer.js b/client/dom/buffer.mjs similarity index 98% rename from client/dom/buffer.js rename to client/dom/buffer.mjs index c6d20ccd..01e8142a 100644 --- a/client/dom/buffer.js +++ b/client/dom/buffer.mjs @@ -1,6 +1,6 @@ /* global CloudCmd*/ -import * as Storage from '#dom/storage'; import tryToPromiseAll from '../../common/try-to-promise-all.js'; +import Storage from './storage.js'; const CLASS = 'cut-file'; const COPY = 'copy'; diff --git a/client/dom/cmd.js b/client/dom/cmd.js deleted file mode 100644 index d228b315..00000000 --- a/client/dom/cmd.js +++ /dev/null @@ -1,83 +0,0 @@ -/* global DOM */ -const SELECTED_FILE = 'selected-file'; -const Cmd = { - getSelectedFiles, - isSelected, - unselectFile, - selectFile, - selectAllFiles, - toggleSelectedFile, - toggleAllSelectedFiles, -}; - -/** - * selected file check - * - * @param currentFile - */ -export function isSelected(currentFile) { - if (!currentFile) - return false; - - return DOM.isContainClass(currentFile, SELECTED_FILE); -} - -/** - * select current file - * @param currentFile - */ -export function selectFile(currentFile) { - const current = currentFile || DOM.getCurrentFile(); - - current.classList.add(SELECTED_FILE); - - return Cmd; -} - -export function unselectFile(currentFile) { - const current = currentFile || DOM.getCurrentFile(); - - current.classList.remove(SELECTED_FILE); - - return Cmd; -} - -export function toggleSelectedFile(currentFile) { - const current = currentFile || DOM.getCurrentFile(); - const name = DOM.getCurrentName(current); - - if (name === '..') - return Cmd; - - current.classList.toggle(SELECTED_FILE); - - return Cmd; -} - -export function toggleAllSelectedFiles() { - DOM - .getAllFiles() - .map(DOM.toggleSelectedFile); - - return Cmd; -} - -export function selectAllFiles() { - DOM - .getAllFiles() - .map(DOM.selectFile); - - return Cmd; -} - -/** - * unified way to get selected files - * - * @currentFile - */ -export function getSelectedFiles() { - const panel = DOM.getPanel(); - const selected = DOM.getByClassAll(SELECTED_FILE, panel); - - return Array.from(selected); -} diff --git a/client/dom/current-file.js b/client/dom/current-file.mjs similarity index 96% rename from client/dom/current-file.js rename to client/dom/current-file.mjs index b8113054..e55acbe9 100644 --- a/client/dom/current-file.js +++ b/client/dom/current-file.mjs @@ -1,8 +1,8 @@ /* global DOM */ /* global CloudCmd */ import createElement from '@cloudcmd/create-element'; -import {getTitle, FS} from '#common/cloudfunc'; -import {encode, decode} from '#common/entity'; +import {encode, decode} from '../../common/entity.js'; +import {getTitle, FS} from '../../common/cloudfunc.mjs'; let Title; @@ -77,7 +77,7 @@ const parseHrefAttribute = (prefix, attribute) => { export const _parseHrefAttribute = parseHrefAttribute; /** - * get current directory path + * get current direcotory path */ export const getCurrentDirPath = (panel = DOM.getPanel()) => { const path = DOM.getByDataName('js-path', panel); @@ -98,7 +98,7 @@ export const getCurrentPath = (currentFile) => { }; /** - * get current directory name + * get current direcotory name */ export const getCurrentDirName = () => { const href = DOM @@ -111,7 +111,7 @@ export const getCurrentDirName = () => { }; /** - * get current directory path + * get current direcotory path */ export const getParentDirPath = (panel) => { const path = DOM.getCurrentDirPath(panel); @@ -125,7 +125,7 @@ export const getParentDirPath = (panel) => { }; /** - * get not current directory path + * get not current direcotory path */ export const getNotCurrentDirPath = () => { const panel = DOM.getPanel({ @@ -153,7 +153,7 @@ export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => { }; /** - * private function that unset currentFile + * private function thet unset currentfile * * @currentFile */ @@ -302,7 +302,6 @@ export const isCurrentIsDir = (currentFile) => { export const getCurrentType = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const el = DOM.getByDataName('js-type', current); - const type = el.className .split(' ') .pop(); diff --git a/client/dom/current-file.spec.js b/client/dom/current-file.spec.mjs similarity index 95% rename from client/dom/current-file.spec.js rename to client/dom/current-file.spec.mjs index 85b18a29..2a576dce 100644 --- a/client/dom/current-file.spec.js +++ b/client/dom/current-file.spec.mjs @@ -1,7 +1,7 @@ import {test, stub} from 'supertape'; import {create} from 'auto-globals'; import wraptile from 'wraptile'; -import * as currentFile from './current-file.js'; +import * as currentFile from './current-file.mjs'; const id = (a) => a; @@ -19,13 +19,11 @@ test('current-file: setCurrentName: setAttribute', (t) => { currentFile.setCurrentName('hello', current); + t.calledWith(setAttribute, ['data-name', 'js-file-aGVsbG8='], 'should call setAttribute'); + globalThis.DOM = DOM; globalThis.CloudCmd = CloudCmd; - t.calledWith(setAttribute, [ - 'data-name', - 'js-file-aGVsbG8=', - ], 'should call setAttribute'); t.end(); }); @@ -40,13 +38,11 @@ test('current-file: setCurrentName: setAttribute: cyrillic', (t) => { currentFile.setCurrentName('ай', current); + t.calledWith(setAttribute, ['data-name', 'js-file-JUQwJUIwJUQwJUI5'], 'should call setAttribute'); + globalThis.DOM = DOM; globalThis.CloudCmd = CloudCmd; - t.calledWith(setAttribute, [ - 'data-name', - 'js-file-JUQwJUIwJUQwJUI5', - ], 'should call setAttribute'); t.end(); }); @@ -74,10 +70,11 @@ test('current-file: emit', (t) => { currentFile.setCurrentName('hello', current); + t.calledWith(emit, ['current-file', current], 'should call emit'); + globalThis.DOM = DOM; globalThis.CloudCmd = CloudCmd; - t.calledWith(emit, ['current-file', current], 'should call emit'); t.end(); }); @@ -93,12 +90,14 @@ test('current-file: setCurrentName: return', (t) => { globalThis.CloudCmd = getCloudCmd(); const current = create(); + const result = currentFile.setCurrentName('hello', current); + t.equal(result, link, 'should return link'); + globalThis.DOM = DOM; globalThis.CloudCmd = CloudCmd; - t.equal(result, link, 'should return link'); t.end(); }); diff --git a/client/dom/dialog.js b/client/dom/dialog.js index b1791d92..eb342221 100644 --- a/client/dom/dialog.js +++ b/client/dom/dialog.js @@ -1,18 +1,26 @@ -import {tryToCatch} from 'try-to-catch'; -import * as smalltalk from 'smalltalk'; +'use strict'; + +const {tryToCatch} = require('try-to-catch'); + +const { + alert, + prompt, + confirm, + progress, +} = require('smalltalk'); const title = 'Cloud Commander'; -export const alert = (...a) => smalltalk.alert(title, ...a, { +module.exports.alert = (...a) => alert(title, ...a, { cancel: false, }); -export const prompt = (...a) => tryToCatch(smalltalk.prompt, title, ...a); -export const confirm = (...a) => tryToCatch(smalltalk.confirm, title, ...a); -export const progress = (...a) => smalltalk.progress(title, ...a); +module.exports.prompt = (...a) => tryToCatch(prompt, title, ...a); +module.exports.confirm = (...a) => tryToCatch(confirm, title, ...a); +module.exports.progress = (...a) => progress(title, ...a); -alert.noFiles = () => { - return smalltalk.alert(title, 'No files selected!', { +module.exports.alert.noFiles = () => { + return alert(title, 'No files selected!', { cancel: false, }); }; diff --git a/client/dom/directory.js b/client/dom/directory.js index efbca119..bedb7e95 100644 --- a/client/dom/directory.js +++ b/client/dom/directory.js @@ -1,10 +1,16 @@ -/* global DOM, CloudCmd */ -import philip from 'philip'; -import * as Dialog from '#dom/dialog'; -import {FS} from '#common/cloudfunc'; -import * as Images from '#dom/images'; +'use strict'; -export const uploadDirectory = (items) => { +/* global CloudCmd */ +const philip = require('philip'); + +const Images = require('./images.mjs'); +const {FS} = require('../../common/cloudfunc.mjs'); +const DOM = require('.'); +const Dialog = require('./dialog'); + +const {getCurrentDirPath: getPathWhenRootEmpty} = DOM; + +module.exports = (items) => { if (items.length) Images.show('top'); @@ -12,7 +18,7 @@ export const uploadDirectory = (items) => { .from(items) .map((item) => item.webkitGetAsEntry()); - const dirPath = DOM.getCurrentDirPath(); + const dirPath = getPathWhenRootEmpty(); const path = dirPath.replace(/\/$/, ''); const progress = Dialog.progress('Uploading...'); diff --git a/client/dom/dom-tree.js b/client/dom/dom-tree.js index fad5ad40..5159cf8b 100644 --- a/client/dom/dom-tree.js +++ b/client/dom/dom-tree.js @@ -1,15 +1,8 @@ -import currify from 'currify'; +'use strict'; -const DOM = { - show, - hide, - getByClass, - getByClassAll, - getByDataName, - getById, - getByTag, - isContainClass, -}; +const currify = require('currify'); + +const DOM = module.exports; /** * check class of element @@ -17,7 +10,7 @@ const DOM = { * @param element * @param className */ -export function isContainClass(element, className) { +const isContainClass = (element, className) => { if (!element) throw Error('element could not be empty!'); @@ -33,60 +26,58 @@ export function isContainClass(element, className) { const {classList} = element; return classList.contains(className); -} +}; +module.exports.isContainClass = isContainClass; /** * Function search element by tag * @param tag - className * @param element - element */ -export function getByTag(tag, element = document) { +module.exports.getByTag = (tag, element = document) => { return element.getElementsByTagName(tag); -} +}; /** * Function search element by id - * @param id - * @param element + * @param Id - id */ -export function getById(id, element = document) { +module.exports.getById = (id, element = document) => { return element.querySelector(`#${id}`); -} +}; /** * Function search first element by class name * @param className - className * @param element - element */ -export function getByClass(className, element = document) { - return DOM.getByClassAll(className, element)[0]; -} +module.exports.getByClass = (className, element = document) => DOM.getByClassAll(className, element)[0]; -export function getByDataName(attribute, element = document) { +module.exports.getByDataName = (attribute, element = document) => { const selector = '[' + 'data-name="' + attribute + '"]'; return element.querySelector(selector); -} +}; /** * Function search element by class name - * @param className - * @param element + * @param pClass - className + * @param element - element */ -export function getByClassAll(className, element) { +module.exports.getByClassAll = (className, element) => { return (element || document).getElementsByClassName(className); -} +}; /** * add class=hidden to element * * @param element */ -export function hide(element) { +module.exports.hide = (element) => { element.classList.add('hidden'); return DOM; -} +}; -export function show(element) { +module.exports.show = (element) => { element.classList.remove('hidden'); return DOM; -} +}; diff --git a/client/dom/dom-tree.spec.js b/client/dom/dom-tree.spec.js index 53b376c7..606df179 100644 --- a/client/dom/dom-tree.spec.js +++ b/client/dom/dom-tree.spec.js @@ -1,16 +1,10 @@ -import {test, stub} from 'supertape'; -import {create} from 'auto-globals'; -import {tryCatch} from 'try-catch'; -import { - isContainClass, - getByTag, - getById, - getByClass, - getByDataName, - getByClassAll, - hide, - show, -} from './dom-tree.js'; +'use strict'; + +const test = require('supertape'); +const {create} = require('auto-globals'); +const {tryCatch} = require('try-catch'); + +const {isContainClass} = require('./dom-tree'); test('dom: isContainClass: no element', (t) => { const [e] = tryCatch(isContainClass); @@ -47,106 +41,3 @@ test('dom: isContainClass: contains: array', (t) => { t.calledWith(contains, [className], 'should call contains'); t.end(); }); - -test('dom: getByTag', (t) => { - const getElementsByTagName = stub(); - const element = { - getElementsByTagName, - }; - - getByTag('div', element); - - t.calledWith(getElementsByTagName, ['div'], 'should call getElementsByTagName'); - t.end(); -}); - -test('dom: getById', (t) => { - const querySelector = stub(); - const element = { - querySelector, - }; - - getById('my-id', element); - - t.calledWith(querySelector, ['#my-id'], 'should call querySelector with id selector'); - t.end(); -}); - -test('dom: getByClassAll', (t) => { - const getElementsByClassName = stub(); - const element = { - getElementsByClassName, - }; - - getByClassAll('my-class', element); - - t.calledWith(getElementsByClassName, ['my-class'], 'should call getElementsByClassName'); - t.end(); -}); - -test('dom: getByClass: calls getByClassAll', (t) => { - const element = { - getElementsByClassName: stub().returns(['first']), - }; - - const result = getByClass('my-class', element); - - t.equal(result, 'first', 'should return first element from class list'); - t.end(); -}); - -test('dom: getByDataName', (t) => { - const querySelector = stub(); - const element = { - querySelector, - }; - - getByDataName('hello', element); - - t.calledWith(querySelector, ['[data-name="hello"]'], 'should call querySelector with data-name selector'); - t.end(); -}); - -test('dom: hide', (t) => { - const add = stub(); - const element = { - classList: { - add, - }, - }; - - hide(element); - - t.calledWith(add, ['hidden'], 'should add hidden class'); - t.end(); -}); - -test('dom: show', (t) => { - const remove = stub(); - const element = { - classList: { - remove, - }, - }; - - show(element); - - t.calledWith(remove, ['hidden'], 'should remove hidden class'); - t.end(); -}); - -test('dom: getByClassAll: without element uses document', (t) => { - const getElementsByClassName = stub(); - const prevDocument = globalThis.document; - - globalThis.document = { - getElementsByClassName, - }; - - getByClassAll('my-class'); - - globalThis.document = prevDocument; - - t.calledWith(getElementsByClassName, ['my-class'], 'should fallback to document when no element'); - t.end(); -}); diff --git a/client/dom/events/event-store.js b/client/dom/events/event-store.js index 1cbfe7a4..ae9ad90a 100644 --- a/client/dom/events/event-store.js +++ b/client/dom/events/event-store.js @@ -1,6 +1,8 @@ +'use strict'; + let list = []; -export const add = (el, name, fn) => { +module.exports.add = (el, name, fn) => { list.push([ el, name, @@ -8,8 +10,8 @@ export const add = (el, name, fn) => { ]); }; -export const clear = () => { +module.exports.clear = () => { list = []; }; -export const get = () => list; +module.exports.get = () => list; diff --git a/client/dom/events/event-store.spec.js b/client/dom/events/event-store.spec.js index 6a623939..82e6752a 100644 --- a/client/dom/events/event-store.spec.js +++ b/client/dom/events/event-store.spec.js @@ -1,5 +1,7 @@ -import {test} from 'supertape'; -import * as eventStore from './event-store.js'; +'use strict'; + +const test = require('supertape'); +const eventStore = require('./event-store'); test('event-store: get', (t) => { const el = {}; diff --git a/client/dom/events/index.js b/client/dom/events/index.mjs similarity index 98% rename from client/dom/events/index.js rename to client/dom/events/index.mjs index c49d2333..0762d560 100644 --- a/client/dom/events/index.js +++ b/client/dom/events/index.mjs @@ -1,5 +1,5 @@ import itype from 'itype'; -import * as EventStore from './event-store.js'; +import EventStore from './event-store.js'; /** * safe add event listener diff --git a/client/dom/files.js b/client/dom/files.js index 395ac0fa..a7de8ff6 100644 --- a/client/dom/files.js +++ b/client/dom/files.js @@ -1,8 +1,11 @@ +'use strict'; + /* global CloudCmd */ -import itype from 'itype'; -import {promisify} from 'es6-promisify'; -import * as load from '#dom/load'; -import * as RESTful from '#dom/rest'; +const itype = require('itype'); +const {promisify} = require('es6-promisify'); + +const load = require('./load'); +const RESTful = require('./rest'); const Promises = {}; const FILES_JSON = 'config|modules'; @@ -13,7 +16,7 @@ const DIR_HTML_FS = `${DIR_HTML}fs/`; const DIR_JSON = '/json/'; const timeout = getTimeoutOnce(2000); -export const get = getFile; +module.exports.get = getFile; function getFile(name) { const type = itype(name); diff --git a/client/dom/images.js b/client/dom/images.mjs similarity index 99% rename from client/dom/images.js rename to client/dom/images.mjs index 75663e72..9682e666 100644 --- a/client/dom/images.js +++ b/client/dom/images.mjs @@ -102,13 +102,13 @@ export function show(position, panel) { /** * hide load image */ -export function hide() { +export const hide = () => { const element = Images.get(); DOM.hide(element); return Images; -} +}; export const setProgress = (value, title) => { const DATA = 'data-progress'; diff --git a/client/dom/index.js b/client/dom/index.js index a82b6f72..27a1a5c6 100644 --- a/client/dom/index.js +++ b/client/dom/index.js @@ -1,121 +1,70 @@ +'use strict'; + /* global CloudCmd */ -import * as load from '#dom/load'; -import * as Files from '#dom/files'; -import * as Dialog from '#dom/dialog'; -import * as Events from '#dom/events'; -import {getExt} from '#common/util'; -import * as Storage from '#dom/storage'; -import * as RESTful from '#dom/rest'; -import * as Images from '#dom/images'; -import renameCurrent from './operations/rename-current.js'; -import * as CurrentFile from './current-file.js'; -import * as DOMTree from './dom-tree.js'; -import * as Cmd from './cmd.js'; -import * as IO from './io/index.js'; -import {uploadDirectory} from './directory.js'; -import * as Buffer from './buffer.js'; -import {loadRemote as _loadRemote} from './load-remote.js'; -import {selectByPattern} from './select-by-pattern.js'; +const Util = require('../../common/util'); -const {assign} = Object; +const Images = require('./images.mjs'); +const RESTful = require('./rest'); +const Storage = require('./storage'); +const renameCurrent = require('./operations/rename-current'); +const CurrentFile = require('./current-file.mjs'); +const DOMTree = require('./dom-tree'); + +const Cmd = module.exports; const DOM = { - getCurrentDirName, - getNotCurrentDirPath, - getParentDirPath, - loadRemote, - loadSocket, - promptNewDir, - promptNewFile, - unselectFiles, - getActiveFiles, - getCurrentDate, - getCurrentSize, - loadCurrentSize, - loadCurrentHash, - setCurrentSize, - getCurrentMode, - getCurrentOwner, - getCurrentData, - getRefreshButton, - getAllFiles, - expandSelection, - shrinkSelection, - setHistory, - getCurrentLink, - getFilenames, - checkStorageHash, - saveDataToStorage, - getFM, - getPanelPosition, - getCSSVar, - getPanel, - getFiles, - showPanel, - hidePanel, - remove, - deleteCurrent, - deleteSelected, - renameCurrent, - scrollIntoViewIfNeeded, - scrollByPages, - changePanel, - getPackerExt, - goToDirectory, - duplicatePanel, - swapPanels, - updateCurrentInfo, -}; - -assign(DOM, { ...DOMTree, ...CurrentFile, ...Cmd, -}); +}; -export const CurrentInfo = {}; +const CurrentInfo = {}; DOM.Images = Images; -DOM.load = load; -DOM.Files = Files; +DOM.load = require('./load'); +DOM.Files = require('./files'); DOM.RESTful = RESTful; -DOM.IO = IO; +DOM.IO = require('./io'); DOM.Storage = Storage; -DOM.Dialog = Dialog; +DOM.Dialog = require('./dialog'); DOM.CurrentInfo = CurrentInfo; -export default DOM; +module.exports = DOM; -DOM.uploadDirectory = uploadDirectory; -DOM.Buffer = Buffer; -DOM.Events = Events; +DOM.uploadDirectory = require('./directory'); +DOM.Buffer = require('./buffer.mjs'); +DOM.Events = require('#dom/events'); + +const loadRemote = require('./load-remote'); +const selectByPattern = require('./select-by-pattern'); const isString = (a) => typeof a === 'string'; +const SELECTED_FILE = 'selected-file'; const TabPanel = { 'js-left': null, 'js-right': null, }; -export function loadRemote(name, options, callback) { - _loadRemote(name, options, callback); +module.exports.loadRemote = (name, options, callback) => { + loadRemote(name, options, callback); return DOM; -} +}; -export function loadSocket(callback) { +module.exports.loadSocket = (callback) => { DOM.loadRemote('socket', { name: 'io', }, callback); return DOM; -} +}; /** * create new folder * */ -export async function promptNewDir() { +module.exports.promptNewDir = async function() { await promptNew('directory'); -} +}; /** * create new file @@ -123,9 +72,9 @@ export async function promptNewDir() { * @typeName * @type */ -export async function promptNewFile() { +module.exports.promptNewFile = async () => { await promptNew('file'); -} +}; async function promptNew(typeName) { const {Dialog} = DOM; @@ -160,9 +109,9 @@ async function promptNew(typeName) { } /** - * get current directory name + * get current direcotory name */ -export function getCurrentDirName() { +module.exports.getCurrentDirName = () => { const href = DOM .getCurrentDirPath() .replace(/\/$/, ''); @@ -170,12 +119,12 @@ export function getCurrentDirName() { const substr = href.substr(href, href.lastIndexOf('/')); return href.replace(`${substr}/`, '') || '/'; -} +}; /** - * get current directory path + * get current direcotory path */ -export function getParentDirPath(panel) { +module.exports.getParentDirPath = (panel) => { const path = DOM.getCurrentDirPath(panel); const dirName = DOM.getCurrentDirName() + '/'; const index = path.lastIndexOf(dirName); @@ -184,36 +133,48 @@ export function getParentDirPath(panel) { return path.slice(0, index); return path; -} +}; /** - * get not current directory path + * get not current direcotory path */ -export function getNotCurrentDirPath() { +module.exports.getNotCurrentDirPath = () => { const panel = DOM.getPanel({ active: false, }); return DOM.getCurrentDirPath(panel); -} +}; + +/** + * unified way to get selected files + * + * @currentFile + */ +module.exports.getSelectedFiles = () => { + const panel = DOM.getPanel(); + const selected = DOM.getByClassAll(SELECTED_FILE, panel); + + return Array.from(selected); +}; /* * unselect all files */ -export function unselectFiles(files) { +module.exports.unselectFiles = (files) => { files = files || DOM.getSelectedFiles(); Array .from(files) .forEach(DOM.toggleSelectedFile); -} +}; /** * get all selected files or current when none selected * * @currentFile */ -export function getActiveFiles() { +module.exports.getActiveFiles = () => { const current = DOM.getCurrentFile(); const files = DOM.getSelectedFiles(); const name = DOM.getCurrentName(current); @@ -222,19 +183,19 @@ export function getActiveFiles() { return [current]; return files; -} +}; -export function getCurrentDate(currentFile) { +module.exports.getCurrentDate = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); return DOM.getByDataName('js-date', current).textContent; -} +}; /** * get size * @currentFile */ -export function getCurrentSize(currentFile) { +module.exports.getCurrentSize = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); /* если это папка - возвращаем слово dir вместо размера*/ @@ -244,13 +205,13 @@ export function getCurrentSize(currentFile) { .replace(/^<|>$/g, ''); return size; -} +}; /** * get size * @currentFile */ -export async function loadCurrentSize(currentFile) { +module.exports.loadCurrentSize = async (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const query = '?size'; const link = DOM.getCurrentPath(current); @@ -266,14 +227,14 @@ export async function loadCurrentSize(currentFile) { Images.hide(); return current; -} +}; /** * load hash * @callback * @currentFile */ -export async function loadCurrentHash(currentFile) { +module.exports.loadCurrentHash = async (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const query = '?hash'; const link = DOM.getCurrentPath(current); @@ -281,45 +242,45 @@ export async function loadCurrentHash(currentFile) { const [, data] = await RESTful.read(link + query); return data; -} +}; /** * set size * @currentFile */ -export function setCurrentSize(size, currentFile) { +module.exports.setCurrentSize = (size, currentFile) => { const current = currentFile || DOM.getCurrentFile(); const sizeElement = DOM.getByDataName('js-size', current); sizeElement.textContent = size; -} +}; /** * @currentFile */ -export function getCurrentMode(currentFile) { +module.exports.getCurrentMode = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const mode = DOM.getByDataName('js-mode', current); return mode.textContent; -} +}; /** * @currentFile */ -export function getCurrentOwner(currentFile) { +module.exports.getCurrentOwner = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const owner = DOM.getByDataName('js-owner', current); return owner.textContent; -} +}; /** * unified way to get current file content * * @param currentFile */ -export async function getCurrentData(currentFile) { +module.exports.getCurrentData = async (currentFile) => { const {Dialog} = DOM; const Info = DOM.CurrentInfo; const current = currentFile || DOM.getCurrentFile(); @@ -361,16 +322,64 @@ export async function getCurrentData(currentFile) { await DOM.saveDataToStorage(path, data, hashNew); return [null, data]; -} +}; /** * unified way to get RefreshButton */ -export function getRefreshButton(panel = DOM.getPanel()) { +module.exports.getRefreshButton = (panel = DOM.getPanel()) => { return DOM.getByDataName('js-refresh', panel); -} +}; -export function getAllFiles() { +/** + * select current file + * @param currentFile + */ +module.exports.selectFile = (currentFile) => { + const current = currentFile || DOM.getCurrentFile(); + + current.classList.add(SELECTED_FILE); + + return Cmd; +}; + +module.exports.unselectFile = (currentFile) => { + const current = currentFile || DOM.getCurrentFile(); + + current.classList.remove(SELECTED_FILE); + + return Cmd; +}; + +module.exports.toggleSelectedFile = (currentFile) => { + const current = currentFile || DOM.getCurrentFile(); + const name = DOM.getCurrentName(current); + + if (name === '..') + return Cmd; + + current.classList.toggle(SELECTED_FILE); + + return Cmd; +}; + +module.exports.toggleAllSelectedFiles = () => { + DOM + .getAllFiles() + .map(DOM.toggleSelectedFile); + + return Cmd; +}; + +module.exports.selectAllFiles = () => { + DOM + .getAllFiles() + .map(DOM.selectFile); + + return Cmd; +}; + +module.exports.getAllFiles = () => { const panel = DOM.getPanel(); const files = DOM.getFiles(panel); const name = DOM.getCurrentName(files[0]); @@ -381,32 +390,32 @@ export function getAllFiles() { return Array .from(files) .slice(i); -} +}; /** * open dialog with expand selection */ -export async function expandSelection() { +module.exports.expandSelection = () => { const msg = 'expand'; const {files} = CurrentInfo; - await selectByPattern(msg, files); -} + selectByPattern(msg, files); +}; /** * open dialog with shrink selection */ -export async function shrinkSelection() { +module.exports.shrinkSelection = () => { const msg = 'shrink'; const {files} = CurrentInfo; - await selectByPattern(msg, files); -} + selectByPattern(msg, files); +}; /** * setting history wrapper */ -export function setHistory(data, title, url) { +module.exports.setHistory = (data, title, url) => { const ret = globalThis.history; const {prefix} = CloudCmd; @@ -416,21 +425,33 @@ export function setHistory(data, title, url) { history.pushState(data, title, url); return ret; -} +}; + +/** + * selected file check + * + * @param currentFile + */ +module.exports.isSelected = (selected) => { + if (!selected) + return false; + + return DOM.isContainClass(selected, SELECTED_FILE); +}; /** * get link from current (or param) file * * @param currentFile - current file by default */ -export function getCurrentLink(currentFile) { +module.exports.getCurrentLink = (currentFile) => { const current = currentFile || DOM.getCurrentFile(); const link = DOM.getByTag('a', current); return link[0]; -} +}; -export function getFilenames(files) { +module.exports.getFilenames = (files) => { if (!files) throw Error('AllFiles could not be empty'); @@ -442,13 +463,17 @@ export function getFilenames(files) { if (name === '..') allFiles.shift(); - return allFiles.map(DOM.getCurrentName); -} + const names = allFiles.map((current) => { + return DOM.getCurrentName(current); + }); + + return names; +}; /** * check storage hash */ -export async function checkStorageHash(name) { +module.exports.checkStorageHash = async (name) => { const nameHash = `${name}-hash`; if (!isString(name)) @@ -460,7 +485,7 @@ export async function checkStorageHash(name) { ]); return [loadHash, storeHash]; -} +}; /** * save data to storage @@ -468,8 +493,9 @@ export async function checkStorageHash(name) { * @param name * @param data * @param hash + * @param callback */ -export async function saveDataToStorage(name, data, hash) { +module.exports.saveDataToStorage = async (name, data, hash) => { const isDir = DOM.isCurrentIsDir(); if (isDir) @@ -484,28 +510,25 @@ export async function saveDataToStorage(name, data, hash) { await Storage.set(nameData, data); return hash; -} +}; -export function getFM() { - const {parentElement} = DOM.getPanel(); - return parentElement; -} +module.exports.getFM = () => DOM.getPanel().parentElement; -export function getPanelPosition(panel) { +module.exports.getPanelPosition = (panel) => { panel = panel || DOM.getPanel(); return panel.dataset.name.replace('js-', ''); -} +}; -export function getCSSVar(name, {body = document.body} = {}) { +module.exports.getCSSVar = (name, {body = document.body} = {}) => { const bodyStyle = getComputedStyle(body); return bodyStyle.getPropertyValue(`--${name}`); -} +}; /** function getting panel active, or passive * @param options = {active: true} */ -export function getPanel(options) { +module.exports.getPanel = (options) => { let files; let panel; let isLeft; @@ -538,17 +561,17 @@ export function getPanel(options) { throw Error('can not find Active Panel!'); return panel; -} +}; -export function getFiles(element) { +module.exports.getFiles = (element) => { const files = DOM.getByDataName('js-files', element); return files.children || []; -} +}; /** * shows panel right or left (or active) */ -export function showPanel(active) { +module.exports.showPanel = (active) => { const panel = DOM.getPanel({ active, }); @@ -559,12 +582,12 @@ export function showPanel(active) { DOM.show(panel); return true; -} +}; /** * hides panel right or left (or active) */ -export function hidePanel(active) { +module.exports.hidePanel = (active) => { const panel = DOM.getPanel({ active, }); @@ -573,27 +596,27 @@ export function hidePanel(active) { return false; return DOM.hide(panel); -} +}; /** * remove child of element - * @param child + * @param pChild * @param element */ -export function remove(child, element) { +module.exports.remove = (child, element) => { const parent = element || document.body; parent.removeChild(child); return DOM; -} +}; /** * remove current file from file table * @param current * */ -export function deleteCurrent(current) { +module.exports.deleteCurrent = (current) => { if (!current) DOM.getCurrentFile(); @@ -607,44 +630,51 @@ export function deleteCurrent(current) { DOM.setCurrentFile(next || prev); parent.removeChild(current); } -} +}; /** * remove selected files from file table * @Selected */ -export function deleteSelected(selected) { +module.exports.deleteSelected = (selected) => { selected = selected || DOM.getSelectedFiles(); if (!selected) return; selected.map(DOM.deleteCurrent); -} +}; /** * rename current file * * @currentFile */ -export function scrollIntoViewIfNeeded(element, center = false) { +module.exports.renameCurrent = renameCurrent; +/** + * unified way to scrollIntoViewIfNeeded + * (native suporte by webkit only) + * @param element + * @param center - to scroll as small as possible param should be false + */ +module.exports.scrollIntoViewIfNeeded = (element, center = false) => { if (!element || !element.scrollIntoViewIfNeeded) return; element.scrollIntoViewIfNeeded(center); -} +}; /* scroll on one page */ -export function scrollByPages(element, pPages) { +module.exports.scrollByPages = (element, pPages) => { const ret = element?.scrollByPages && pPages; if (ret) element.scrollByPages(pPages); return ret; -} +}; -export function changePanel() { +module.exports.changePanel = () => { const Info = CurrentInfo; let panel = DOM.getPanel(); @@ -690,21 +720,18 @@ export function changePanel() { CloudCmd.emit('active-dir', Info.dirPath); return DOM; -} +}; -export function getPackerExt(type) { +module.exports.getPackerExt = (type) => { if (type === 'zip') return '.zip'; return '.tar.gz'; -} +}; -export async function goToDirectory(overrides = {}) { +module.exports.goToDirectory = async (overrides = {}) => { const {Dialog} = DOM; - const { - prompt = Dialog.prompt, - changeDir = CloudCmd.changeDir, - } = overrides; + const {prompt = Dialog.prompt, changeDir = CloudCmd.changeDir} = overrides; const msg = 'Go to directory:'; const {dirPath} = CurrentInfo; @@ -715,9 +742,9 @@ export async function goToDirectory(overrides = {}) { return; await changeDir(path); -} +}; -export async function duplicatePanel() { +module.exports.duplicatePanel = async () => { const Info = CurrentInfo; const {isDir} = Info; const panel = Info.panelPassive; @@ -736,9 +763,9 @@ export async function duplicatePanel() { panel, noCurrent, }); -} +}; -export async function swapPanels() { +module.exports.swapPanels = async () => { const Info = CurrentInfo; const { panel, @@ -769,9 +796,11 @@ export async function swapPanels() { const el = Info.files[currentIndex]; DOM.setCurrentFile(el); -} +}; -export function updateCurrentInfo(currentFile) { +module.exports.CurrentInfo = CurrentInfo; + +module.exports.updateCurrentInfo = (currentFile) => { const info = DOM.CurrentInfo; const current = currentFile || DOM.getCurrentFile(); const files = current.parentElement; @@ -787,7 +816,7 @@ export function updateCurrentInfo(currentFile) { info.dirPath = DOM.getCurrentDirPath(); info.parentDirPath = DOM.getParentDirPath(); info.element = current; - info.ext = getExt(name); + info.ext = Util.getExt(name); info.files = Array.from(files.children); info.filesPassive = Array.from(filesPassive); info.first = files.firstChild; @@ -808,4 +837,4 @@ export function updateCurrentInfo(currentFile) { .name .replace('js-', ''); info.isOnePanel = info.panel.getAttribute('data-name') === info.panelPassive.getAttribute('data-name'); -} +}; diff --git a/client/dom/index.spec.js b/client/dom/index.spec.js index 268efe5f..e3a4b4bc 100644 --- a/client/dom/index.spec.js +++ b/client/dom/index.spec.js @@ -1,5 +1,9 @@ -import {test, stub} from 'supertape'; -import {getCSSVar, goToDirectory} from '#dom'; +'use strict'; + +require('css-modules-require-hook/preset'); + +const {test, stub} = require('supertape'); +const {getCSSVar, goToDirectory} = require('./index'); globalThis.CloudCmd = {}; diff --git a/client/dom/io/index.js b/client/dom/io/index.js index 192769e6..a17360ae 100644 --- a/client/dom/io/index.js +++ b/client/dom/io/index.js @@ -1,35 +1,24 @@ -import {FS} from '#common/cloudfunc'; -import {sendRequest as _sendRequest} from './send-request.js'; +'use strict'; -const {assign} = Object; +const {FS} = require('../../../common/cloudfunc.mjs'); +const _sendRequest = require('./send-request'); const imgPosition = { top: true, }; -export const remove = async (url, data, overrides = {}) => { - const { - sendRequest = _sendRequest, - } = overrides; - - const request = { +module.exports.delete = async (url, data) => { + return await _sendRequest({ method: 'DELETE', url: FS + url, + data, imgPosition: { top: Boolean(data), }, - }; - - if (data) - assign(request, { - data, - url: `${request.url}?files`, - }); - - return await sendRequest(request); + }); }; -export const patch = async (url, data) => { +module.exports.patch = async (url, data) => { return await _sendRequest({ method: 'PATCH', url: FS + url, @@ -38,7 +27,7 @@ export const patch = async (url, data) => { }); }; -export const write = async (url, data) => { +module.exports.write = async (url, data) => { return await _sendRequest({ method: 'PUT', url: FS + url, @@ -47,7 +36,7 @@ export const write = async (url, data) => { }); }; -export const createDirectory = async (url, overrides = {}) => { +module.exports.createDirectory = async (url, overrides = {}) => { const { sendRequest = _sendRequest, } = overrides; @@ -59,7 +48,7 @@ export const createDirectory = async (url, overrides = {}) => { }); }; -export const read = async (url, dataType = 'text') => { +module.exports.read = async (url, dataType = 'text') => { const notLog = !url.includes('?'); return await _sendRequest({ @@ -70,7 +59,7 @@ export const read = async (url, dataType = 'text') => { }); }; -export const copy = async (from, to, names) => { +module.exports.copy = async (from, to, names) => { return await _sendRequest({ method: 'PUT', url: '/copy', @@ -83,7 +72,7 @@ export const copy = async (from, to, names) => { }); }; -export const pack = async (data) => { +module.exports.pack = async (data) => { return await _sendRequest({ method: 'PUT', url: '/pack', @@ -91,7 +80,7 @@ export const pack = async (data) => { }); }; -export const extract = async (data) => { +module.exports.extract = async (data) => { return await _sendRequest({ method: 'PUT', url: '/extract', @@ -99,7 +88,7 @@ export const extract = async (data) => { }); }; -export const move = async (from, to, names) => { +module.exports.move = async (from, to, names) => { return await _sendRequest({ method: 'PUT', url: '/move', @@ -112,7 +101,7 @@ export const move = async (from, to, names) => { }); }; -export const rename = async (from, to) => { +module.exports.rename = async (from, to) => { return await _sendRequest({ method: 'PUT', url: '/rename', @@ -124,7 +113,7 @@ export const rename = async (from, to) => { }); }; -export const Config = { +module.exports.Config = { read: async () => { return await _sendRequest({ method: 'GET', @@ -144,7 +133,7 @@ export const Config = { }, }; -export const Markdown = { +module.exports.Markdown = { read: async (url) => { return await _sendRequest({ method: 'GET', diff --git a/client/dom/io/index.spec.js b/client/dom/io/index.spec.js index 8e9e2d08..19ebe5bd 100644 --- a/client/dom/io/index.spec.js +++ b/client/dom/io/index.spec.js @@ -1,5 +1,7 @@ -import {test, stub} from 'supertape'; -import * as io from './index.js'; +'use strict'; + +const {test, stub} = require('supertape'); +const io = require('.'); test('client: dom: io', (t) => { const sendRequest = stub(); @@ -19,43 +21,3 @@ test('client: dom: io', (t) => { t.calledWith(sendRequest, [expected]); t.end(); }); - -test('client: dom: io: remove: no files', async (t) => { - const sendRequest = stub(); - - await io.remove('/hello', null, { - sendRequest, - }); - - const expected = { - imgPosition: { - top: false, - }, - method: 'DELETE', - url: '/fs/hello', - }; - - t.calledWith(sendRequest, [expected]); - t.end(); -}); - -test('client: dom: io: remove: files', async (t) => { - const sendRequest = stub(); - const files = ['world']; - - await io.remove('/hello', files, { - sendRequest, - }); - - const expected = { - imgPosition: { - top: true, - }, - data: ['world'], - method: 'DELETE', - url: '/fs/hello?files', - }; - - t.calledWith(sendRequest, [expected]); - t.end(); -}); diff --git a/client/dom/io/send-request.js b/client/dom/io/send-request.js index 177d34b5..c61544f1 100644 --- a/client/dom/io/send-request.js +++ b/client/dom/io/send-request.js @@ -1,9 +1,12 @@ -/* global CloudCmd */ -import {promisify} from 'es6-promisify'; -import * as Images from '#dom/images'; -import * as load from '#dom/load'; +'use strict'; -export const sendRequest = promisify((params, callback) => { +/* global CloudCmd */ +const {promisify} = require('es6-promisify'); + +const Images = require('../images.mjs'); +const load = require('../load'); + +module.exports = promisify((params, callback) => { const p = params; const {prefixURL} = CloudCmd; @@ -37,8 +40,7 @@ export const sendRequest = promisify((params, callback) => { }); }); -export const _replaceHash = replaceHash; - +module.exports._replaceHash = replaceHash; function replaceHash(url) { /* * if we send ajax request - diff --git a/client/dom/io/send-request.spec.js b/client/dom/io/send-request.spec.js index f8a960d8..e8d0449a 100644 --- a/client/dom/io/send-request.spec.js +++ b/client/dom/io/send-request.spec.js @@ -1,5 +1,7 @@ -import {test} from 'supertape'; -import {_replaceHash} from './send-request.js'; +'use strict'; + +const test = require('supertape'); +const {_replaceHash} = require('./send-request'); test('cloudcmd: client: io: replaceHash', (t) => { const url = '/hello/####world'; diff --git a/client/dom/load-remote.js b/client/dom/load-remote.js index 5be8e327..b1b798a8 100644 --- a/client/dom/load-remote.js +++ b/client/dom/load-remote.js @@ -1,58 +1,56 @@ -/* global CloudCmd */ -import {callbackify} from 'node:util'; -import {rendy} from 'rendy'; -import itype from 'itype'; -import * as load from 'load.js'; -import {tryToCatch} from 'try-to-catch'; -import {findObjByNameInArr} from '#common/util'; -import * as Files from '#dom/files'; +'use strict'; -export const loadRemote = callbackify(async (name, options) => { +/* global CloudCmd */ +const rendy = require('rendy'); +const itype = require('itype'); +const load = require('load.js'); +const {tryToCatch} = require('try-to-catch'); + +const {findObjByNameInArr} = require('../../common/util'); + +const Files = require('./files'); + +module.exports = (name, options, callback = options) => { const {prefix, config} = CloudCmd; const o = options; if (o.name && window[o.name]) - return; + return callback(); - const modules = await Files.get('modules'); - - const online = config('online') && navigator.onLine; - const module = findObjByNameInArr(modules.remote, name); - - const isArray = itype.array(module.local); - const {version} = module; - - let remoteTmpls; - let local; - - if (isArray) { - remoteTmpls = module.remote; - ({local} = module); - } else { - remoteTmpls = [module.remote]; - local = [module.local]; - } - - const localURL = []; - - for (const url of local) { - localURL.push(prefix + url); - } - - const remoteURL = []; - - for (const tmpl of remoteTmpls) { - remoteURL.push(rendy(tmpl, { - version, - })); - } - - if (online) { - const [e] = await tryToCatch(load.parallel, remoteURL); + Files.get('modules').then(async (modules) => { + const online = config('online') && navigator.onLine; + const module = findObjByNameInArr(modules.remote, name); - if (!e) - return; - } - - await load.parallel(localURL); -}); + const isArray = itype.array(module.local); + const {version} = module; + + let remoteTmpls; + let local; + + if (isArray) { + remoteTmpls = module.remote; + ({local} = module); + } else { + remoteTmpls = [module.remote]; + local = [module.local]; + } + + const localURL = local.map((url) => prefix + url); + + const remoteURL = remoteTmpls.map((tmpl) => { + return rendy(tmpl, { + version, + }); + }); + + if (online) { + const [e] = await tryToCatch(load.parallel, remoteURL); + + if (!e) + return callback(); + } + + const [e] = await tryToCatch(load.parallel, localURL); + callback(e); + }); +}; diff --git a/client/dom/load.js b/client/dom/load.js index 8c7011c1..d060a92c 100644 --- a/client/dom/load.js +++ b/client/dom/load.js @@ -1,16 +1,19 @@ -import itype from 'itype'; -import jonny from 'jonny'; -import Emitify from 'emitify'; -import exec from 'execon'; -import * as Images from '#dom/images'; +'use strict'; +const itype = require('itype'); +const jonny = require('jonny'); +const Emitify = require('emitify'); +const exec = require('execon'); +const Images = require('./images.mjs'); + +module.exports.getIdBySrc = getIdBySrc; /** * Function gets id by src * @param src * * Example: http://domain.com/1.js -> 1_js */ -export function getIdBySrc(src) { +function getIdBySrc(src) { const isStr = itype.string(src); if (!isStr) @@ -34,7 +37,7 @@ export function getIdBySrc(src) { * * @param params */ -export const ajax = (params) => { +module.exports.ajax = (params) => { const p = params; const isObject = itype.object(p.data); const isArray = itype.array(p.data); @@ -91,11 +94,11 @@ export const ajax = (params) => { xhr.send(data); }; -export const put = (url, body) => { +module.exports.put = (url, body) => { const emitter = Emitify(); const xhr = new XMLHttpRequest(); - url = encodeURI(url).replace(/#/g, '#'); + url = encodeURI(url).replace(/#/g, '%23'); xhr.open('put', url, true); diff --git a/client/dom/operations/rename-current.js b/client/dom/operations/rename-current.js index 8b6536d3..ab658edf 100644 --- a/client/dom/operations/rename-current.js +++ b/client/dom/operations/rename-current.js @@ -1,11 +1,15 @@ -/* global CloudCmd */ -import capitalize from 'just-capitalize'; -import * as _Dialog from '#dom/dialog'; -import * as Storage from '#dom/storage'; -import * as RESTful from '#dom/rest'; -import * as _currentFile from '../current-file.js'; +'use strict'; -export default async (current, overrides = {}) => { +/* global CloudCmd */ +const capitalize = require('just-capitalize'); + +const _Dialog = require('../dialog'); +const Storage = require('../storage'); +const RESTful = require('../rest'); + +const _currentFile = require('../current-file.mjs'); + +module.exports = async (current, overrides = {}) => { const { refresh = CloudCmd.refresh, Dialog = _Dialog, diff --git a/client/dom/operations/rename-current.spec.js b/client/dom/operations/rename-current.spec.js index a5ebb415..4a3c7fec 100644 --- a/client/dom/operations/rename-current.spec.js +++ b/client/dom/operations/rename-current.spec.js @@ -1,5 +1,8 @@ -import {test, stub} from 'supertape'; -import renameCurrent from './rename-current.js'; +'use strict'; + +const {test, stub} = require('supertape'); + +const renameCurrent = require('./rename-current'); test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => { const current = {}; diff --git a/client/dom/rest.js b/client/dom/rest.js index 3cf2fb66..7596f620 100644 --- a/client/dom/rest.js +++ b/client/dom/rest.js @@ -1,8 +1,12 @@ -import {tryToCatch} from 'try-to-catch'; -import * as Dialog from '#dom/dialog'; -import * as Images from '#dom/images'; -import {encode} from '#common/entity'; -import * as IO from './io/index.js'; +'use strict'; + +const {tryToCatch} = require('try-to-catch'); + +const {encode} = require('../../common/entity'); + +const Images = require('./images.mjs'); +const IO = require('./io'); +const Dialog = require('./dialog'); const handleError = (promise) => async (...args) => { const [e, data] = await tryToCatch(promise, ...args); @@ -18,23 +22,23 @@ const handleError = (promise) => async (...args) => { return [e, data]; }; -export const remove = handleError(IO.remove); -export const patch = handleError(IO.patch); -export const write = handleError(IO.write); -export const createDirectory = handleError(IO.createDirectory); -export const read = handleError(IO.read); -export const copy = handleError(IO.copy); -export const pack = handleError(IO.pack); -export const extract = handleError(IO.extract); -export const move = handleError(IO.move); -export const rename = handleError(IO.rename); +module.exports.delete = handleError(IO.delete); +module.exports.patch = handleError(IO.patch); +module.exports.write = handleError(IO.write); +module.exports.createDirectory = handleError(IO.createDirectory); +module.exports.read = handleError(IO.read); +module.exports.copy = handleError(IO.copy); +module.exports.pack = handleError(IO.pack); +module.exports.extract = handleError(IO.extract); +module.exports.move = handleError(IO.move); +module.exports.rename = handleError(IO.rename); -export const Config = { +module.exports.Config = { read: handleError(IO.Config.read), write: handleError(IO.Config.write), }; -export const Markdown = { +module.exports.Markdown = { read: handleError(IO.Markdown.read), render: handleError(IO.Markdown.render), }; diff --git a/client/dom/select-by-pattern.js b/client/dom/select-by-pattern.js index 0bada6ef..2f2cb22d 100644 --- a/client/dom/select-by-pattern.js +++ b/client/dom/select-by-pattern.js @@ -1,14 +1,13 @@ -import {alert, prompt} from '#dom/dialog'; -import {getRegExp} from '#common/util'; -import {getCurrentName} from './current-file.js'; -import { - isSelected, - toggleSelectedFile, -} from './cmd.js'; +'use strict'; let SelectType = '*.*'; -export const selectByPattern = async (msg, files) => { +const {getRegExp} = require('../../common/util'); +const {alert, prompt} = require('./dialog'); + +const DOM = require('.'); + +module.exports = async (msg, files) => { if (!files) return; @@ -24,21 +23,21 @@ export const selectByPattern = async (msg, files) => { let matches = 0; for (const current of files) { - const name = getCurrentName(current); + const name = DOM.getCurrentName(current); if (name === '..' || !regExp.test(name)) continue; ++matches; - let selected = isSelected(current); + let isSelected = DOM.isSelected(current); const shouldSel = msg === 'expand'; if (shouldSel) - selected = !selected; + isSelected = !isSelected; - if (selected) - toggleSelectedFile(current); + if (isSelected) + DOM.toggleSelectedFile(current); } if (!matches) diff --git a/client/dom/storage.js b/client/dom/storage.js index d5cf231f..1fa6e8fe 100644 --- a/client/dom/storage.js +++ b/client/dom/storage.js @@ -1,26 +1,28 @@ +'use strict'; + const {parse, stringify} = JSON; -export const set = (name, data) => { +module.exports.set = async (name, data) => { localStorage.setItem(name, data); }; -export const setJson = (name, data) => { +module.exports.setJson = async (name, data) => { localStorage.setItem(name, stringify(data)); }; -export const get = (name) => { +module.exports.get = async (name) => { return localStorage.getItem(name); }; -export const getJson = (name) => { +module.exports.getJson = async (name) => { const data = localStorage.getItem(name); return parse(data); }; -export const clear = () => { +module.exports.clear = () => { localStorage.clear(); }; -export const remove = (item) => { +module.exports.remove = (item) => { localStorage.removeItem(item); }; diff --git a/client/dom/storage.spec.js b/client/dom/storage.spec.js index c538952d..0b281ffc 100644 --- a/client/dom/storage.spec.js +++ b/client/dom/storage.spec.js @@ -1,5 +1,8 @@ -import {test, stub} from 'supertape'; -import * as storage from '#dom/storage'; +'use strict'; + +const {test, stub} = require('supertape'); + +const storage = require('./storage'); const {stringify} = JSON; diff --git a/client/dom/upload-files.js b/client/dom/upload-files.js index 1c37fe1b..a1206282 100644 --- a/client/dom/upload-files.js +++ b/client/dom/upload-files.js @@ -1,19 +1,24 @@ -/* global CloudCmd */ -import {eachSeries} from 'execon'; -import wraptile from 'wraptile'; -import * as load from '#dom/load'; -import {alert} from '#dom/dialog'; -import {FS} from '#common/cloudfunc'; -import * as Images from '#dom/images'; -import {getCurrentDirPath} from './current-file.js'; +'use strict'; +/* global CloudCmd */ +const {eachSeries} = require('execon'); +const wraptile = require('wraptile'); + +const load = require('./load'); +const Images = require('./images.mjs'); +const {alert} = require('./dialog'); + +const {FS} = require('../../common/cloudfunc.mjs'); + +const {getCurrentDirPath: getPathWhenRootEmpty} = require('.'); const loadFile = wraptile(_loadFile); + const onEnd = wraptile(_onEnd); -export const uploadFiles = (dir, files) => { +module.exports = (dir, files) => { if (!files) { files = dir; - dir = getCurrentDirPath(); + dir = getPathWhenRootEmpty(); } const n = files.length; diff --git a/client/get-json-from-file-table.js b/client/get-json-from-file-table.mjs similarity index 100% rename from client/get-json-from-file-table.js rename to client/get-json-from-file-table.mjs diff --git a/client/key/binder.js b/client/key/binder.js index 6aec97ea..2e858d76 100644 --- a/client/key/binder.js +++ b/client/key/binder.js @@ -1,4 +1,6 @@ -export const createBinder = () => { +'use strict'; + +module.exports.createBinder = () => { let binded = false; return { diff --git a/client/key/binder.spec.js b/client/key/binder.spec.js deleted file mode 100644 index f9d54fdd..00000000 --- a/client/key/binder.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -import {test} from 'supertape'; -import {createBinder} from './binder.js'; - -test('client: key: binder: isBind: default', (t) => { - const binder = createBinder(); - - t.notOk(binder.isBind(), 'should not be bind by default'); - t.end(); -}); - -test('client: key: binder: setBind', (t) => { - const binder = createBinder(); - - binder.setBind(); - - t.ok(binder.isBind(), 'should be bind'); - t.end(); -}); - -test('client: key: binder: unsetBind', (t) => { - const binder = createBinder(); - - binder.setBind(); - binder.unsetBind(); - - t.notOk(binder.isBind(), 'should not be bind'); - t.end(); -}); diff --git a/client/key/index.js b/client/key/index.mjs similarity index 97% rename from client/key/index.js rename to client/key/index.mjs index 283a7f11..f963350e 100644 --- a/client/key/index.js +++ b/client/key/index.mjs @@ -2,8 +2,8 @@ import clipboard from '@cloudcmd/clipboard'; import {fullstore} from 'fullstore'; import * as Events from '#dom/events'; -import * as Buffer from '../dom/buffer.js'; -import * as KEY from './key.js'; +import * as Buffer from '../dom/buffer.mjs'; +import * as KEY from './key.mjs'; import _vim from './vim/index.js'; import setCurrentByChar from './set-current-by-char.js'; import {createBinder} from './binder.js'; @@ -13,8 +13,8 @@ const Chars = fullstore(); const toggleVim = (keyCode, overrides = {}) => { const {_config, config} = overrides; - if (!config('vim') && keyCode === KEY.ESC) - _config('vim', true); + if (keyCode === KEY.ESC) + _config('vim', !config('vim')); }; const isUndefined = (a) => typeof a === 'undefined'; @@ -63,7 +63,7 @@ async function listener(event, overrides = {}) { const {keyCode} = event; - // strange chrome bug calls listener twice + // strange chrome bug calles listener twice // in second time event misses a lot fields if (isUndefined(event.altKey)) return; @@ -442,14 +442,6 @@ async function _switchKey(event) { break; - case KEY.L: - if (ctrlMeta && shift) { - CloudCmd.logOut(); - event.preventDefault(); - } - - break; - case KEY.M: if (ctrlMeta) { if (config('vim')) diff --git a/client/key/index.spec.js b/client/key/index.spec.js index bec92b31..b3735fe7 100644 --- a/client/key/index.spec.js +++ b/client/key/index.spec.js @@ -1,9 +1,15 @@ -import autoGlobals from 'auto-globals'; -import supertape from 'supertape'; -import {ESC} from './key.js'; -import {Key, _listener} from './index.js'; -import {getDOM, getCloudCmd} from './vim/globals.fixture.js'; +'use strict'; +require('css-modules-require-hook/preset'); + +const autoGlobals = require('auto-globals'); +const supertape = require('supertape'); + +const {ESC} = require('./key.mjs'); + +const {Key, _listener} = require('./index.mjs'); + +const {getDOM, getCloudCmd} = require('./vim/globals.fixture'); const test = autoGlobals(supertape); const {stub} = supertape; @@ -37,7 +43,6 @@ 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', diff --git a/client/key/key.js b/client/key/key.mjs similarity index 98% rename from client/key/key.js rename to client/key/key.mjs index 6772e34c..66120e74 100644 --- a/client/key/key.js +++ b/client/key/key.mjs @@ -22,7 +22,6 @@ export const D = 68; export const G = 71; export const J = 74; export const K = 75; -export const L = 76; export const M = 77; export const O = 79; export const P = 80; diff --git a/client/key/set-current-by-char.js b/client/key/set-current-by-char.js index 92764f11..ab9329f3 100644 --- a/client/key/set-current-by-char.js +++ b/client/key/set-current-by-char.js @@ -1,11 +1,14 @@ /* global DOM */ -import {escapeRegExp} from '#common/util'; -export default function setCurrentByChar(char, charStore) { +'use strict'; + +const {escapeRegExp} = require('../../common/util'); + +module.exports = function setCurrentByChar(char, charStore) { const Info = DOM.CurrentInfo; let firstByName; let skipCount = 0; - let set = false; + let setted = false; let i = 0; const escapeChar = escapeRegExp(char); @@ -33,7 +36,7 @@ export default function setCurrentByChar(char, charStore) { const byName = DOM.getCurrentByName(name); if (!skipCount) { - set = true; + setted = true; DOM.setCurrentFile(byName); return true; @@ -50,8 +53,8 @@ export default function setCurrentByChar(char, charStore) { .filter(not(isRoot)) .some(setCurrent); - if (!set) { + if (!setted) { DOM.setCurrentFile(firstByName); charStore([char]); } -} +}; diff --git a/client/key/vim/find.js b/client/key/vim/find.js index e8db825a..d8b517c1 100644 --- a/client/key/vim/find.js +++ b/client/key/vim/find.js @@ -1,10 +1,12 @@ -import {fullstore} from 'fullstore'; -import limier from 'limier'; +'use strict'; + +const {fullstore} = require('fullstore'); +const limier = require('limier'); const searchStore = fullstore([]); const searchIndex = fullstore(0); -export const find = (value, names) => { +module.exports.find = (value, names) => { const result = limier(value, names); searchStore(result); @@ -13,7 +15,7 @@ export const find = (value, names) => { return result; }; -export const findNext = () => { +module.exports.findNext = () => { const names = searchStore(); const index = next(searchIndex(), names.length); @@ -21,7 +23,7 @@ export const findNext = () => { return names[searchIndex()]; }; -export const findPrevious = () => { +module.exports.findPrevious = () => { const names = searchStore(); const index = previous(searchIndex(), names.length); @@ -29,8 +31,8 @@ export const findPrevious = () => { return names[index]; }; -export const _next = next; -export const _previous = previous; +module.exports._next = next; +module.exports._previous = previous; function next(index, length) { if (index === length - 1) diff --git a/client/key/vim/find.spec.js b/client/key/vim/find.spec.js index 191a3652..74cc7fb1 100644 --- a/client/key/vim/find.spec.js +++ b/client/key/vim/find.spec.js @@ -1,15 +1,14 @@ -import test from 'supertape'; -import {getDOM} from './globals.fixture.js'; -import { - _next, - _previous, - find, - findNext, - findPrevious, -} from './find.js'; +'use strict'; + +const test = require('supertape'); +const dir = './'; + +const {getDOM} = require('./globals.fixture'); globalThis.DOM = getDOM(); +const {_next, _previous} = require(`${dir}find`); + test('cloudcmd: client: vim: _next', (t) => { const result = _next(1, 2); @@ -17,39 +16,9 @@ test('cloudcmd: client: vim: _next', (t) => { t.end(); }); -test('cloudcmd: client: vim: _next: increment', (t) => { - const result = _next(0, 2); - - t.equal(result, 1, 'should return 1'); - t.end(); -}); - test('cloudcmd: client: vim: _previous', (t) => { const result = _previous(0, 2); t.equal(result, 1, 'should return 1'); t.end(); }); - -test('cloudcmd: client: vim: _previous: decrement', (t) => { - const result = _previous(1, 2); - - t.equal(result, 0, 'should return 0'); - t.end(); -}); - -test('cloudcmd: client: vim: findNext: after find', (t) => { - find('a', ['alpha', 'beta', 'apple']); - const result = findNext(); - - t.equal(result, 'beta', 'should return next found name'); - t.end(); -}); - -test('cloudcmd: client: vim: findPrevious: after find', (t) => { - find('a', ['alpha', 'beta', 'apple']); - const result = findPrevious(); - - t.equal(result, 'apple', 'should return previous found name'); - t.end(); -}); diff --git a/client/key/vim/globals.fixture.js b/client/key/vim/globals.fixture.js index c49894d7..3882e477 100644 --- a/client/key/vim/globals.fixture.js +++ b/client/key/vim/globals.fixture.js @@ -1,6 +1,8 @@ +'use strict'; + const noop = () => {}; -export const getDOM = () => { +module.exports.getDOM = () => { const prompt = Promise.resolve.bind(Promise); const CurrentInfo = { element: {}, @@ -32,7 +34,7 @@ export const getDOM = () => { }; }; -export const getCloudCmd = () => { +module.exports.getCloudCmd = () => { const show = () => {}; return { diff --git a/client/key/vim/index.js b/client/key/vim/index.js index 981f6f19..c7d21a27 100644 --- a/client/key/vim/index.js +++ b/client/key/vim/index.js @@ -1,11 +1,15 @@ -import vim from './vim.js'; -import * as finder from './find.js'; -import { +'use strict'; + +/* global CloudCmd */ +/* global DOM */ +const vim = require('./vim'); +const finder = require('./find'); +const { setCurrent, selectFileNotParent, -} from './set-current.js'; +} = require('./set-current'); -export default (key, event, overrides = {}) => { +module.exports = (key, event, overrides = {}) => { const defaults = { ...globalThis.DOM, ...globalThis.CloudCmd, @@ -33,13 +37,13 @@ const getOperations = (event, deps) => { prompt = globalThis.DOM.Dialog.prompt, preventDefault = event?.preventDefault?.bind(event), stopImmediatePropagation = event?.preventDefault?.bind(event), - promptNewFile = globalThis.DOM.promptNewFile, + promptNewFile = DOM.promptNewFile, + toggleSelectedFile, Buffer = {}, createFindNext = _createFindNext, createFindPrevious = _createFindPrevious, createMakeFile = _createMakeFile, - renameCurrent, } = deps; return { @@ -55,26 +59,15 @@ const getOperations = (event, deps) => { setCurrentByName, }), escape: unselectFiles, - rename: () => { - event.preventDefault(); - renameCurrent(); - }, + remove: () => { Operation.show('delete'); }, - operationCopy: () => { - event.preventDefault(); - Operation.show('copy'); - }, - operationMove: () => { - event.preventDefault(); - Operation.show('move'); - }, makeDirectory: () => { event.stopImmediatePropagation(); event.preventDefault(); - globalThis.DOM.promptNewDir(); + DOM.promptNewDir(); }, terminal: () => { @@ -138,7 +131,7 @@ const getOperations = (event, deps) => { }; }; -export const selectFile = selectFileNotParent; +module.exports.selectFile = selectFileNotParent; const _createFindPrevious = (overrides = {}) => () => { const {setCurrentByName} = overrides; diff --git a/client/key/vim/index.spec.js b/client/key/vim/index.spec.js index 231c6577..2a1b1274 100644 --- a/client/key/vim/index.spec.js +++ b/client/key/vim/index.spec.js @@ -1,15 +1,25 @@ -import {test, stub} from 'supertape'; -import {getDOM, getCloudCmd} from './globals.fixture.js'; -import vim, {selectFile as vimSelectFile} from './index.js'; -import * as finder from './find.js'; +'use strict'; + +const {join} = require('node:path'); +const {test, stub} = require('supertape'); + +const dir = '../'; + +const pathVim = join(dir, 'vim'); + +const {getDOM, getCloudCmd} = require('./globals.fixture'); globalThis.DOM = getDOM(); globalThis.CloudCmd = getCloudCmd(); +const vim = require('./index.js'); + const {assign} = Object; const {DOM} = globalThis; const {Buffer} = DOM; +const {reRequire, stopAll} = require('mock-require'); + test('cloudcmd: client: key: set next file: no', (t) => { const element = {}; const setCurrentFile = stub(); @@ -309,7 +319,7 @@ test('cloudcmd: client: key: selectFile: ..', (t) => { const selectFile = stub(); const current = {}; - vimSelectFile(current, { + vim.selectFile(current, { selectFile, getCurrentName, }); @@ -323,7 +333,7 @@ test('cloudcmd: client: key: selectFile', (t) => { const getCurrentName = stub().returns('x'); const current = {}; - vimSelectFile(current, { + vim.selectFile(current, { selectFile, getCurrentName, }); @@ -448,7 +458,6 @@ test('cloudcmd: client: key: set first file current: ^', async (t) => { test('cloudcmd: client: key: visual', (t) => { const element = {}; const toggleSelectedFile = stub(); - const Info = { element, }; @@ -465,7 +474,6 @@ test('cloudcmd: client: key: visual', (t) => { test('cloudcmd: client: key: ESC', (t) => { const element = {}; const unselectFiles = stub(); - const Info = { element, }; @@ -511,7 +519,6 @@ test('cloudcmd: client: key: Enter', async (t) => { test('cloudcmd: client: key: /', (t) => { const preventDefault = stub(); const element = {}; - const Info = { element, files: [], @@ -546,12 +553,16 @@ test('cloudcmd: client: find', (t) => { setCurrentByName, }); + const vim = reRequire(pathVim); + const event = { preventDefault: stub(), }; vim('/', event); + stopAll(); + t.notCalled(setCurrentByName); t.end(); }); @@ -584,6 +595,7 @@ test('cloudcmd: client: key: N', (t) => { }); test('cloudcmd: client: key: make directory', async (t) => { + const vim = reRequire(pathVim); const {DOM} = globalThis; assign(DOM, { @@ -658,99 +670,3 @@ test('cloudcmd: client: vim: edit', async (t) => { t.calledWithNoArgs(CloudCmd.EditFileVim.show); t.end(); }); - -test('cloudcmd: client: vim: rename', async (t) => { - const DOM = getDOM(); - const renameCurrent = stub(); - - assign(DOM, { - renameCurrent, - }); - - const event = { - preventDefault: stub(), - }; - - await vim('rr', event, DOM); - - t.calledWithNoArgs(renameCurrent); - t.end(); -}); - -test('cloudcmd: client: key: cc: operationCopy', (t) => { - const show = stub(); - const preventDefault = stub(); - - const Operation = { - show, - }; - - const event = { - preventDefault, - }; - - vim('c', event, { - Operation, - }); - - vim('c', event, { - Operation, - }); - - t.calledWith(show, ['copy'], 'should show copy operation'); - t.end(); -}); - -test('cloudcmd: client: key: mm: operationMove', (t) => { - const show = stub(); - const preventDefault = stub(); - - const Operation = { - show, - }; - - const event = { - preventDefault, - }; - - vim('m', event, { - Operation, - }); - - vim('m', event, { - Operation, - }); - - t.calledWith(show, ['move'], 'should show move operation'); - t.end(); -}); - -test('cloudcmd: client: key: n: findNext: real', (t) => { - const setCurrentByName = stub(); - - finder.find('a', ['alpha', 'beta', 'apple']); - - const event = {}; - - vim('n', event, { - setCurrentByName, - }); - - t.calledWith(setCurrentByName, ['beta'], 'should set current by next found name'); - t.end(); -}); - -test('cloudcmd: client: key: N: findPrevious: real', (t) => { - const setCurrentByName = stub(); - - finder.find('a', ['alpha', 'beta', 'apple']); - - const event = {}; - - vim('N', event, { - setCurrentByName, - }); - - t.calledWith(setCurrentByName, ['apple'], 'should set current by previous found name'); - t.end(); -}); diff --git a/client/key/vim/set-current.js b/client/key/vim/set-current.js index a2fc8646..b761cc07 100644 --- a/client/key/vim/set-current.js +++ b/client/key/vim/set-current.js @@ -1,5 +1,8 @@ +'use strict'; + /* global DOM */ -export function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) { +module.exports.selectFileNotParent = selectFileNotParent; +function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) { const name = getCurrentName(current); if (name === '..') @@ -8,7 +11,7 @@ export function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) selectFile(current); } -export const setCurrent = (sibling, {count, isVisual, isDelete}, {Info, setCurrentFile, unselectFiles, Operation}) => { +module.exports.setCurrent = (sibling, {count, isVisual, isDelete}, {Info, setCurrentFile, unselectFiles, Operation}) => { let current = Info.element; const select = isVisual ? selectFileNotParent : unselectFiles; diff --git a/client/key/vim/vim.js b/client/key/vim/vim.js index 95a27caf..175f36ac 100644 --- a/client/key/vim/vim.js +++ b/client/key/vim/vim.js @@ -1,5 +1,6 @@ -import {fullstore} from 'fullstore'; +'use strict'; +const {fullstore} = require('fullstore'); const store = fullstore(''); const visual = fullstore(false); @@ -20,7 +21,7 @@ const rmFirst = (a) => { const noop = () => {}; -export default (key, operations = {}) => { +module.exports = (key, operations = {}) => { const prevStore = store(); const isVisual = visual(); const value = store(prevStore.concat(key)); @@ -40,9 +41,6 @@ export default (key, operations = {}) => { makeDirectory = noop, terminal = noop, edit = noop, - operationCopy = noop, - operationMove = noop, - rename = noop, } = operations; if (key === 'Enter') @@ -114,26 +112,11 @@ export default (key, operations = {}) => { return end(); } - if (value === 'cc') { - operationCopy(); - return end(); - } - - if (value === 'mm') { - operationMove(); - return end(); - } - if (value === 'mf') { makeFile(); return end(); } - if (value === 'rr') { - rename(); - return end(); - } - if (key === 'd' && (visual() || prevStore === 'd')) { stopVisual(); remove(); diff --git a/client/key/vim/vim.spec.js b/client/key/vim/vim.spec.js index 02fd52d4..f4ea0683 100644 --- a/client/key/vim/vim.spec.js +++ b/client/key/vim/vim.spec.js @@ -1,5 +1,8 @@ -import {test, stub} from 'supertape'; -import vim from './vim.js'; +'use strict'; + +const {test, stub} = require('supertape'); + +const vim = require('./vim'); test('vim: no operations', (t) => { const result = vim('hello', {}); @@ -43,28 +46,6 @@ test('vim: ^', (t) => { t.end(); }); -test('vim: cc', (t) => { - const operationCopy = stub(); - - vim('cc', { - operationCopy, - }); - - t.calledWithNoArgs(operationCopy); - t.end(); -}); - -test('vim: mm', (t) => { - const operationMove = stub(); - - vim('mm', { - operationMove, - }); - - t.calledWithNoArgs(operationMove); - t.end(); -}); - test('vim: w', (t) => { const moveNext = stub(); diff --git a/client/listeners/get-index.js b/client/listeners/get-index.mjs similarity index 100% rename from client/listeners/get-index.js rename to client/listeners/get-index.mjs diff --git a/client/listeners/get-index.spec.js b/client/listeners/get-index.spec.mjs similarity index 90% rename from client/listeners/get-index.spec.js rename to client/listeners/get-index.spec.mjs index 0380cdc1..ae05159f 100644 --- a/client/listeners/get-index.spec.js +++ b/client/listeners/get-index.spec.mjs @@ -1,5 +1,5 @@ import test from 'supertape'; -import {getIndex} from './get-index.js'; +import {getIndex} from './get-index.mjs'; test('cloudcmd: client: listeners: getIndex: not found', (t) => { const array = ['hello']; diff --git a/client/listeners/get-range.js b/client/listeners/get-range.mjs similarity index 100% rename from client/listeners/get-range.js rename to client/listeners/get-range.mjs diff --git a/client/listeners/get-range.spec.js b/client/listeners/get-range.spec.mjs similarity index 96% rename from client/listeners/get-range.spec.js rename to client/listeners/get-range.spec.mjs index 77c950f4..38b8ab32 100644 --- a/client/listeners/get-range.spec.js +++ b/client/listeners/get-range.spec.mjs @@ -1,5 +1,5 @@ import test from 'supertape'; -import {getRange} from './get-range.js'; +import {getRange} from './get-range.mjs'; test('cloudcmd: client: listeners: getRange: direct', (t) => { const expected = [ diff --git a/client/listeners/index.js b/client/listeners/index.mjs similarity index 97% rename from client/listeners/index.js rename to client/listeners/index.mjs index babc1e0d..130972cb 100644 --- a/client/listeners/index.js +++ b/client/listeners/index.mjs @@ -5,15 +5,15 @@ import currify from 'currify'; import {tryToCatch} from 'try-to-catch'; import clipboard from '@cloudcmd/clipboard'; import * as Events from '#dom/events'; -import {uploadFiles} from '#dom/upload-files'; -import {FS} from '#common/cloudfunc'; -import {getRange} from './get-range.js'; -import {getIndex} from './get-index.js'; +import {getRange} from './get-range.mjs'; +import uploadFiles from '../dom/upload-files.js'; +import {FS} from '../../common/cloudfunc.mjs'; +import {getIndex} from './get-index.mjs'; const NBSP_REG = RegExp(String.fromCharCode(160), 'g'); const SPACE = ' '; -export async function init() { +export const init = async () => { contextMenu(); dragndrop(); unload(); @@ -21,7 +21,7 @@ export async function init() { resize(); header(); await config(); -} +}; const unselect = (event) => { const isMac = /Mac/.test(globalThis.navigator.platform); @@ -55,7 +55,6 @@ let EXT; function header() { const fm = DOM.getFM(); const isDataset = (el) => el.dataset; - const isPanel = (el) => { return /^js-(left|right)$/.test(el.dataset.name); }; @@ -310,12 +309,8 @@ function onDragStart(event) { link.href = prefixURL + '/pack' + Info.path + EXT; } - event.dataTransfer.setData( - 'DownloadURL', - 'application/octet-stream' + ':' + name + - ':' + - link, - ); + event.dataTransfer.setData('DownloadURL', 'application/octet-stream' + ':' + name + + ':' + link); } function getLIElement(element) { diff --git a/client/load-module.js b/client/load-module.mjs similarity index 100% rename from client/load-module.js rename to client/load-module.mjs diff --git a/client/modules/cloud.js b/client/modules/cloud.js index cfe0d708..53a6d9e9 100644 --- a/client/modules/cloud.js +++ b/client/modules/cloud.js @@ -1,32 +1,32 @@ /* global CloudCmd, filepicker */ -import exec from 'execon'; -import currify from 'currify'; -import load from 'load.js'; -import {ajax} from '#dom/load'; -import * as Files from '#dom/files'; -import * as Images from '#dom/images'; +'use strict'; + +const exec = require('execon'); +const currify = require('currify'); +const load = require('load.js'); + +const {ajax} = require('../dom/load'); + +const Files = require('../dom/files'); +const Images = require('../dom/images.mjs'); const {log} = CloudCmd; const upload = currify(_upload); const Name = 'Cloud'; -CloudCmd[Name] = { - init, - uploadFile, - saveFile, -}; +CloudCmd[Name] = module.exports; -export async function init() { +module.exports.init = async () => { const [modules] = await loadFiles(); const {key} = modules.data.FilePicker; filepicker.setKey(key); Images.hide(); -} +}; -export function uploadFile(filename, data) { +module.exports.uploadFile = (filename, data) => { const mimetype = ''; filepicker.store(data, { @@ -35,11 +35,11 @@ export function uploadFile(filename, data) { }, (fpFile) => { filepicker.exportFile(fpFile, log, log); }); -} +}; -export function saveFile(callback) { +module.exports.saveFile = (callback) => { filepicker.pick(upload(callback)); -} +}; function _upload(callback, file) { const {url, filename} = file; diff --git a/client/modules/command-line.js b/client/modules/command-line.js index 7a1c3993..a0c000d9 100644 --- a/client/modules/command-line.js +++ b/client/modules/command-line.js @@ -1,14 +1,18 @@ +'use strict'; + /* global CloudCmd */ -import * as Dialog from '#dom/dialog'; +CloudCmd.CommandLine = exports; -export function init() {} -CloudCmd.CommandLine = { - init, - show, - hide, -}; +const Dialog = require('../dom/dialog'); -export async function show() { +const noop = () => {}; + +module.exports.init = noop; + +module.exports.show = show; +module.exports.hide = hide; + +async function show() { const [, cmd] = await Dialog.prompt('Command Line', ''); const TERMINAL = '^(t|terminal)'; @@ -28,4 +32,4 @@ export async function show() { } } -export function hide() {} +function hide() {} diff --git a/client/modules/config/index.js b/client/modules/config/index.mjs similarity index 92% rename from client/modules/config/index.js rename to client/modules/config/index.mjs index dfa4c105..91b8e7e3 100644 --- a/client/modules/config/index.js +++ b/client/modules/config/index.mjs @@ -1,5 +1,5 @@ -import '../../../css/config.css'; -import {rendy} from 'rendy'; +/* global CloudCmd, DOM, io */ +import rendy from 'rendy'; import currify from 'currify'; import wraptile from 'wraptile'; import squad from 'squad'; @@ -8,12 +8,11 @@ import {tryToCatch} from 'try-to-catch'; import load from 'load.js'; import createElement from '@cloudcmd/create-element'; import * as Events from '#dom/events'; -import * as Files from '#dom/files'; -import {getTitle} from '#common/cloudfunc'; -import * as Images from '#dom/images'; -import * as input from './input.js'; - -const {CloudCmd, DOM} = globalThis; +import '../../../css/config.css'; +import * as input from './input.mjs'; +import * as Images from '../../dom/images.mjs'; +import Files from '../../dom/files.js'; +import {getTitle} from '../../../common/cloudfunc.mjs'; const {Dialog, setTitle} = DOM; @@ -41,7 +40,7 @@ let Template; const loadCSS = load.css; -export async function init() { +export const init = async () => { if (!CloudCmd.config('configDialog')) return; @@ -57,7 +56,7 @@ export async function init() { ]); initSocket(); -} +}; const {config, Key} = CloudCmd; @@ -79,7 +78,7 @@ function initSocket() { const ONE_MINUTE = 60 * 1000; - const socket = globalThis.io.connect(href + prefixSocket + '/config', { + const socket = io.connect(href + prefixSocket + '/config', { reconnectionAttempts: Infinity, reconnectionDelay: ONE_MINUTE, path: `${prefix}/socket.io`, @@ -133,7 +132,6 @@ async function fillTemplate() { columns, theme, configAuth, - configPort, ...obj } = input.convert(config); @@ -143,7 +141,6 @@ async function fillTemplate() { obj[`${columns}-selected`] = 'selected'; obj[`${theme}-selected`] = 'selected'; obj.configAuth = configAuth ? '' : 'hidden'; - obj.configPort = configPort ? '' : 'hidden'; const innerHTML = rendy(Template, obj); diff --git a/client/modules/config/input.js b/client/modules/config/input.mjs similarity index 72% rename from client/modules/config/input.js rename to client/modules/config/input.mjs index 34a4be69..430f7e51 100644 --- a/client/modules/config/input.js +++ b/client/modules/config/input.mjs @@ -1,9 +1,7 @@ -import {encode} from '#common/entity'; +import currify from 'currify'; -const isBool = (a) => typeof a === 'boolean'; -const isString = (a) => typeof a === 'string'; - -const {keys} = Object; +const isType = currify((type, object, name) => type === typeof object[name]); +const isBool = isType('boolean'); export function getElementByName(selector, element) { const str = `[data-name="js-${selector}"]`; @@ -21,19 +19,13 @@ export const getName = (element) => { export const convert = (config) => { const result = config; + const array = Object.keys(config); - for (const name of keys(config)) { + const filtered = array.filter(isBool(config)); + + for (const name of filtered) { const item = config[name]; - - if (isBool(item)) { - result[name] = setState(item); - continue; - } - - if (isString(item)) { - result[name] = encode(item); - continue; - } + result[name] = setState(item); } return result; diff --git a/client/modules/config/input.spec.js b/client/modules/config/input.spec.js deleted file mode 100644 index 9d0866cd..00000000 --- a/client/modules/config/input.spec.js +++ /dev/null @@ -1,140 +0,0 @@ -import {test, stub} from 'supertape'; -import { - convert, - getName, - getValue, - setValue, -} from './input.js'; - -test('cloudcmd: client: config: input: convert', (t) => { - const result = convert({ - name: 'hello ', - }); - - const expected = { - name: 'hello <world>', - }; - - t.deepEqual(result, expected); - t.end(); -}); - -test('cloudcmd: client: config: input: convert: bool', (t) => { - const result = convert({ - auth: true, - }); - - const expected = { - auth: ' checked', - }; - - t.deepEqual(result, expected); - t.end(); -}); - -test('cloudcmd: client: config: input: convert: bool false', (t) => { - const result = convert({ - auth: false, - }); - - const expected = { - auth: '', - }; - - t.deepEqual(result, expected); - t.end(); -}); - -test('cloudcmd: client: config: input: getName', (t) => { - const getAttribute = stub().returns('js-hello'); - const element = { - getAttribute, - }; - - const result = getName(element); - - t.equal(result, 'hello', 'should strip js- prefix'); - t.end(); -}); - -test('cloudcmd: client: config: input: getValue: checkbox', (t) => { - const querySelector = stub().returns({ - type: 'checkbox', - checked: true, - }); - - const element = { - querySelector, - }; - - const result = getValue('auth', element); - - t.ok(result, 'should return checked value'); - t.end(); -}); - -test('cloudcmd: client: config: input: getValue: number', (t) => { - const querySelector = stub().returns({ - type: 'number', - value: '42', - }); - - const element = { - querySelector, - }; - - const result = getValue('port', element); - - t.equal(result, 42, 'should return number'); - t.end(); -}); - -test('cloudcmd: client: config: input: getValue: default', (t) => { - const querySelector = stub().returns({ - type: 'text', - value: 'hello', - }); - - const element = { - querySelector, - }; - - const result = getValue('name', element); - - t.equal(result, 'hello', 'should return value as is'); - t.end(); -}); - -test('cloudcmd: client: config: input: setValue: checkbox', (t) => { - const el = { - type: 'checkbox', - checked: false, - }; - - const querySelector = stub().returns(el); - const element = { - querySelector, - }; - - setValue('auth', true, element); - - t.ok(el.checked, 'should set checked'); - t.end(); -}); - -test('cloudcmd: client: config: input: setValue: default', (t) => { - const el = { - type: 'text', - value: 'old', - }; - - const querySelector = stub().returns(el); - const element = { - querySelector, - }; - - setValue('name', 'new', element); - - t.equal(el.value, 'new', 'should set value'); - t.end(); -}); diff --git a/client/modules/contact.js b/client/modules/contact.js index 48c7bd19..c6266de0 100644 --- a/client/modules/contact.js +++ b/client/modules/contact.js @@ -1,34 +1,36 @@ /* global CloudCmd */ /* global DOM */ -import olark from '@cloudcmd/olark'; -import * as Images from '#dom/images'; -CloudCmd.Contact = { - init, - show, - hide, -}; +'use strict'; + +CloudCmd.Contact = exports; + +const olark = require('@cloudcmd/olark'); +const Images = require('../dom/images.mjs'); const {Events} = DOM; const {Key} = CloudCmd; -export function init() { +module.exports.show = show; +module.exports.hide = hide; + +module.exports.init = () => { Events.addKey(onKey); olark.identify('6216-545-10-4223'); olark('api.box.onExpand', show); olark('api.box.onShow', show); olark('api.box.onShrink', hide); -} +}; -export function show() { +function show() { Key.unsetBind(); Images.hide(); olark('api.box.expand'); } -export function hide() { +function hide() { Key.setBind(); olark('api.box.hide'); } diff --git a/client/modules/edit-file-vim.js b/client/modules/edit-file-vim.js index 16d557ae..0edd203b 100644 --- a/client/modules/edit-file-vim.js +++ b/client/modules/edit-file-vim.js @@ -1,12 +1,9 @@ -import * as Events from '#dom/events'; +'use strict'; -const {CloudCmd} = globalThis; +/* global CloudCmd */ +CloudCmd.EditFileVim = exports; -CloudCmd.EditFileVim = { - init, - show, - hide, -}; +const Events = require('#dom/events'); const {Key} = CloudCmd; @@ -18,11 +15,11 @@ const ConfigView = { }, }; -export async function init() { +module.exports.init = async () => { await CloudCmd.EditFile(); -} +}; -export async function show() { +module.exports.show = async () => { Events.addKey(listener); const editFile = await CloudCmd.EditFile.show(ConfigView); @@ -30,9 +27,11 @@ export async function show() { editFile .getEditor() .setKeyMap('vim'); -} +}; -export function hide() { +module.exports.hide = hide; + +function hide() { CloudCmd.Edit.hide(); } diff --git a/client/modules/edit-file.js b/client/modules/edit-file.js index 83a07e36..59050a3c 100644 --- a/client/modules/edit-file.js +++ b/client/modules/edit-file.js @@ -1,15 +1,12 @@ -/* global CloudCmd, DOM*/ -import Format from 'format-io'; -import {fullstore} from 'fullstore'; -import exec from 'execon'; -import {supermenu} from 'supermenu'; +'use strict'; -CloudCmd.EditFile = { - init, - show, - hide, - isChanged, -}; +/* global CloudCmd, DOM*/ +CloudCmd.EditFile = exports; + +const Format = require('format-io'); +const {fullstore} = require('fullstore'); +const exec = require('execon'); +const supermenu = require('supermenu'); const Info = DOM.CurrentInfo; @@ -29,7 +26,7 @@ const ConfigView = { }, }; -export async function init() { +module.exports.init = async () => { isLoading(true); await CloudCmd.Edit(); @@ -39,7 +36,7 @@ export async function init() { setListeners(editor); isLoading(false); -} +}; function getName() { const {name, isDir} = Info; @@ -50,7 +47,7 @@ function getName() { return name; } -export async function show(options) { +module.exports.show = async (options) => { if (isLoading()) return; @@ -89,9 +86,11 @@ export async function show(options) { CloudCmd.Edit.show(optionsEdit); return CloudCmd.Edit; -} +}; -export function hide() { +module.exports.hide = hide; + +function hide() { CloudCmd.Edit.hide(); } @@ -177,7 +176,9 @@ function setMsgChanged(name) { MSG_CHANGED = `Do you want to save changes to ${name}?`; } -export async function isChanged() { +module.exports.isChanged = isChanged; + +async function isChanged() { const editor = CloudCmd.Edit.getEditor(); const is = editor.isChanged(); diff --git a/client/modules/edit-names-vim.js b/client/modules/edit-names-vim.js index 8b3b2f89..0dbd92b2 100644 --- a/client/modules/edit-names-vim.js +++ b/client/modules/edit-names-vim.js @@ -1,12 +1,9 @@ -import * as Events from '#dom/events'; +'use strict'; -const {CloudCmd} = globalThis; +/* global CloudCmd */ +CloudCmd.EditNamesVim = exports; -CloudCmd.EditNamesVim = { - init, - show, - hide, -}; +const Events = require('#dom/events'); const {Key} = CloudCmd; const ConfigView = { @@ -17,20 +14,22 @@ const ConfigView = { }, }; -export async function init() { +module.exports.init = async () => { await CloudCmd.EditNames(); -} +}; -export function show() { +module.exports.show = () => { Events.addKey(listener); CloudCmd.EditNames .show(ConfigView) .getEditor() .setKeyMap('vim'); -} +}; -export function hide() { +module.exports.hide = hide; + +function hide() { CloudCmd.Edit.hide(); } diff --git a/client/modules/edit-names.js b/client/modules/edit-names.js index 7bc29054..710ed7ea 100644 --- a/client/modules/edit-names.js +++ b/client/modules/edit-names.js @@ -1,16 +1,13 @@ -import {tryToCatch} from 'try-to-catch'; -import exec from 'execon'; -import {supermenu} from 'supermenu'; -import {multiRename} from 'multi-rename'; +'use strict'; -const {CloudCmd, DOM} = globalThis; +const {tryToCatch} = require('try-to-catch'); -CloudCmd.EditNames = { - init, - show, - hide, - isChanged, -}; +/* global CloudCmd, DOM */ +CloudCmd.EditNames = exports; + +const exec = require('execon'); +const supermenu = require('supermenu'); +const {multiRename} = require('multi-rename'); const Info = DOM.CurrentInfo; const {Dialog} = DOM; @@ -25,13 +22,13 @@ const ConfigView = { }, }; -export async function init() { +module.exports.init = async () => { await CloudCmd.Edit(); setListeners(); -} +}; -export function show(options) { +module.exports.show = (options) => { const names = getActiveNames().join('\n'); const config = { ...ConfigView, @@ -53,7 +50,7 @@ export function show(options) { CloudCmd.Edit.show(config); return CloudCmd.Edit; -} +}; async function keyListener(event) { const ctrl = event.ctrlKey; @@ -63,10 +60,7 @@ async function keyListener(event) { if (ctrlMeta && event.keyCode === Key.S) { hide(); - return; - } - - if (ctrlMeta && event.keyCode === Key.P) { + } else if (ctrlMeta && event.keyCode === Key.P) { const [, pattern] = await Dialog.prompt('Apply pattern:', '[n][e]'); pattern && applyPattern(pattern); } @@ -83,7 +77,9 @@ function getActiveNames() { return DOM.getFilenames(DOM.getActiveFiles()); } -export function hide() { +module.exports.hide = hide; + +function hide() { CloudCmd.Edit.hide(); } @@ -205,7 +201,9 @@ function setMenu(event) { Menu.show(position.x, position.y); } -export async function isChanged() { +module.exports.isChanged = isChanged; + +async function isChanged() { const editor = CloudCmd.Edit.getEditor(); const msg = 'Apply new names?'; diff --git a/client/modules/edit.js b/client/modules/edit.js index 593a11cb..ecefaad8 100644 --- a/client/modules/edit.js +++ b/client/modules/edit.js @@ -1,28 +1,23 @@ /* global CloudCmd */ -import {montag} from 'montag'; -import {promisify} from 'es6-promisify'; -import {tryToCatch} from 'try-to-catch'; -import createElement from '@cloudcmd/create-element'; -import load from 'load.js'; -import {MAX_FILE_SIZE as maxSize} from '#common/cloudfunc'; -import {time, timeEnd} from '#common/util'; -export function getEditor() { - return editor; -} +'use strict'; +const montag = require('montag'); + +const {promisify} = require('es6-promisify'); +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 {time, timeEnd} = require('../../common/util'); +const getEditor = () => editor; const isFn = (a) => typeof a === 'function'; const loadJS = load.js; const Name = 'Edit'; -CloudCmd[Name] = { - init, - show, - hide, - getEditor, - getElement, -}; +CloudCmd[Name] = exports; const EditorName = CloudCmd.config('editor'); @@ -38,12 +33,12 @@ const ConfigView = { }, }; -export async function init() { +module.exports.init = async () => { const element = create(); await CloudCmd.View(); await loadFiles(element); -} +}; function create() { const element = createElement('div', { @@ -84,7 +79,7 @@ function initConfig(options = {}) { return config; } -export function show(options) { +module.exports.show = (options) => { if (Loading) return; @@ -93,15 +88,15 @@ export function show(options) { getEditor().setOptions({ fontSize: 16, }); -} +}; -export function getElement() { - return Element; -} +module.exports.getEditor = getEditor; -export function hide() { +module.exports.getElement = () => Element; + +module.exports.hide = () => { CloudCmd.View.hide(); -} +}; const loadFiles = async (element) => { const prefix = `${CloudCmd.prefix}/${EditorName}`; diff --git a/client/modules/help.js b/client/modules/help.js index 74657a90..242b7c16 100644 --- a/client/modules/help.js +++ b/client/modules/help.js @@ -1,18 +1,18 @@ -import * as Images from '#dom/images'; +'use strict'; -const {CloudCmd} = globalThis; +/* global CloudCmd */ +CloudCmd.Help = exports; -CloudCmd.Help = { - init, - show, - hide, +const Images = require('../dom/images.mjs'); + +module.exports.init = () => { + Images.show.load('top'); }; -export function init() { - Images.show.load('top'); -} +module.exports.show = show; +module.exports.hide = hide; -export function show() { +function show() { const positionLoad = 'top'; const relative = true; @@ -22,6 +22,6 @@ export function show() { }); } -export function hide() { +function hide() { CloudCmd.View.hide(); } diff --git a/client/modules/konsole.js b/client/modules/konsole.js index e9a465af..ae5bc42c 100644 --- a/client/modules/konsole.js +++ b/client/modules/konsole.js @@ -1,20 +1,18 @@ +'use strict'; + /* global CloudCmd */ /* global Util */ /* global DOM */ /* global Console */ -import exec from 'execon'; -import currify from 'currify'; -import {tryToCatch} from 'try-to-catch'; -import {js as loadJS} from 'load.js'; -import createElement from '@cloudcmd/create-element'; -import * as Images from '#dom/images'; +CloudCmd.Konsole = exports; -CloudCmd.Konsole = { - init, - show, - hide, -}; +const exec = require('execon'); +const currify = require('currify'); +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 {Dialog, CurrentInfo: Info} = DOM; const rmLastSlash = (a) => a.replace(/\/$/, '') || '/'; @@ -29,7 +27,7 @@ const Name = 'Konsole'; let Element; let Loaded; -export async function init() { +module.exports.init = async () => { if (!config('console')) return; @@ -38,13 +36,13 @@ export async function init() { await CloudCmd.View(); await load(); await create(); -} +}; -export function hide() { +module.exports.hide = () => { CloudCmd.View.hide(); -} +}; -export const clear = () => { +module.exports.clear = () => { konsole.clear(); }; @@ -111,7 +109,7 @@ function authCheck(konsole) { }); } -export function show(callback) { +module.exports.show = (callback) => { if (!Loaded) return; @@ -124,7 +122,7 @@ export function show(callback) { exec(callback); }, }); -} +}; const load = async () => { Util.time(`${Name} load`); diff --git a/client/modules/markdown.js b/client/modules/markdown.js index c94c64e8..9dc224af 100644 --- a/client/modules/markdown.js +++ b/client/modules/markdown.js @@ -1,26 +1,26 @@ -import createElement from '@cloudcmd/create-element'; -import * as Images from '#dom/images'; -import {Markdown} from '#dom/rest'; -import {alert} from '#dom/dialog'; +'use strict'; -const {CloudCmd} = globalThis; +/* global CloudCmd */ +CloudCmd.Markdown = exports; -CloudCmd.Markdown = { - init, - show, - hide, -}; +const createElement = require('@cloudcmd/create-element'); -export async function init() { +const Images = require('../dom/images.mjs'); +const {Markdown} = require('../dom/rest'); +const {alert} = require('../dom/dialog'); + +module.exports.init = async () => { Images.show.load('top'); await CloudCmd.View(); -} +}; -export function hide() { +module.exports.show = show; + +module.exports.hide = () => { CloudCmd.View.hide(); -} +}; -export async function show(name, options = {}) { +async function show(name, options = {}) { const {positionLoad, relative} = options; Images.show.load(positionLoad); diff --git a/client/modules/menu/cloudmenu.js b/client/modules/menu/cloudmenu.mjs similarity index 83% rename from client/modules/menu/cloudmenu.js rename to client/modules/menu/cloudmenu.mjs index 8f7c1e68..1fbe5165 100644 --- a/client/modules/menu/cloudmenu.js +++ b/client/modules/menu/cloudmenu.mjs @@ -1,4 +1,4 @@ -import {supermenu} from 'supermenu'; +import supermenu from 'supermenu'; const noop = () => {}; const {CloudCmd} = globalThis; @@ -14,9 +14,8 @@ export const createCloudMenu = async (fm, options, menuData) => { async function loadMenu() { if (CloudCmd.config('menu') === 'aleman') { - const {prefix} = CloudCmd; const {host, protocol} = globalThis.location; - const url = `${protocol}//${host}${prefix}/node_modules/aleman/menu/menu.js`; + const url = `${protocol}//${host}/node_modules/aleman/menu/menu.js`; const {createMenu} = await import(/* webpackIgnore: true */url); return createMenu; diff --git a/client/modules/menu/index.js b/client/modules/menu/index.js index 7235591e..facb385a 100644 --- a/client/modules/menu/index.js +++ b/client/modules/menu/index.js @@ -1,11 +1,15 @@ -import exec from 'execon'; -import wrap from 'wraptile'; -import createElement from '@cloudcmd/create-element'; -import {getIdBySrc} from '#dom/load'; -import * as RESTful from '#dom/rest'; -import {FS} from '#common/cloudfunc'; +/* global CloudCmd, DOM */ + +'use strict'; + +const exec = require('execon'); +const wrap = require('wraptile'); +const createElement = require('@cloudcmd/create-element'); + +const {FS} = require('../../../common/cloudfunc.mjs'); +const {getIdBySrc} = require('../../dom/load'); +const RESTful = require('../../dom/rest'); -const {CloudCmd, DOM} = globalThis; const {config, Key} = CloudCmd; const { @@ -23,15 +27,11 @@ let MenuShowedName; let MenuContext; let MenuContextFile; -export const ENABLED = false; +module.exports.ENABLED = false; -CloudCmd.Menu = { - init, - show, - hide, -}; +CloudCmd.Menu = exports; -export async function init() { +module.exports.init = async () => { const {isAuth, menuDataFile} = getFileMenuData(); const fm = DOM.getFM(); @@ -45,7 +45,7 @@ export async function init() { type: 'file', }); - const {createCloudMenu} = await import('./cloudmenu.js'); + const {createCloudMenu} = await import('./cloudmenu.mjs'); const {name} = fm.dataset; @@ -56,21 +56,23 @@ export async function init() { MenuContextFile.addContextMenuListener(); Events.addKey(listener); -} +}; -export function hide() { +module.exports.hide = hide; + +function hide() { MenuContext.hide(); MenuContextFile.hide(); } -export function show(position) { +module.exports.show = (position) => { const {x, y} = getPosition(position); MenuContext.show(x, y); MenuContextFile.show(x, y); Images.hide(); -} +}; function getPosition(position) { if (position) @@ -296,7 +298,7 @@ function download(type) { * no need in hash so we escape # * and all other characters, like "%" */ - const encodedPath = encodeURI(path).replace(/#/g, '#'); + const encodedPath = encodeURI(path).replace(/#/g, '%23'); const id = getIdBySrc(path); let src; diff --git a/client/modules/operation/format.js b/client/modules/operation/format.js index c639799e..ffed26f4 100644 --- a/client/modules/operation/format.js +++ b/client/modules/operation/format.js @@ -1,4 +1,6 @@ -export const format = (operation, from, to) => { +'use strict'; + +module.exports = (operation, from, to) => { if (!to) return `${operation} ${from}`; diff --git a/client/modules/operation/get-next-current-name.js b/client/modules/operation/get-next-current-name.js index 63445888..d0f120d5 100644 --- a/client/modules/operation/get-next-current-name.js +++ b/client/modules/operation/get-next-current-name.js @@ -1,9 +1,11 @@ -import currify from 'currify'; +'use strict'; + +const currify = require('currify'); const not = currify((array, value) => !array.includes(value)); const notOneOf = currify((a, b) => a.filter(not(b))); -export const getNextCurrentName = (currentName, names, removedNames) => { +module.exports = (currentName, names, removedNames) => { const i = names.indexOf(currentName); const nextNames = notOneOf(names, removedNames); diff --git a/client/modules/operation/index.js b/client/modules/operation/index.mjs similarity index 97% rename from client/modules/operation/index.js rename to client/modules/operation/index.mjs index f349f7d8..8e8149e5 100644 --- a/client/modules/operation/index.js +++ b/client/modules/operation/index.mjs @@ -4,10 +4,10 @@ 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'; -import {removeExtension} from './remove-extension.js'; -import {setListeners} from './set-listeners.js'; -import {getNextCurrentName} from './get-next-current-name.js'; +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'; const {DOM, CloudCmd} = globalThis; @@ -190,11 +190,11 @@ function getPacker(type) { return packTarFn; } -export function hide() { +export const hide = () => { CloudCmd.View.hide(); -} +}; -export function show(operation, data) { +export const show = (operation, data) => { if (!Loaded) return; @@ -215,7 +215,7 @@ export function show(operation, data) { if (operation === 'extract') return Operation.extract(); -} +}; Operation.copy = processFiles({ type: 'copy', diff --git a/client/modules/operation/remove-extension.js b/client/modules/operation/remove-extension.js index e6ce2c46..5e98727d 100644 --- a/client/modules/operation/remove-extension.js +++ b/client/modules/operation/remove-extension.js @@ -1,6 +1,8 @@ -import {getExt} from '#common/util'; +'use strict'; -export const removeExtension = (name) => { +const {getExt} = require('../../../common/util'); + +module.exports = (name) => { const ext = getExtension(name); return name.replace(ext, ''); diff --git a/client/modules/operation/remove-extension.spec.js b/client/modules/operation/remove-extension.spec.js index fa11e9e2..9896e735 100644 --- a/client/modules/operation/remove-extension.spec.js +++ b/client/modules/operation/remove-extension.spec.js @@ -1,5 +1,7 @@ -import test from 'supertape'; -import {removeExtension} from './remove-extension.js'; +'use strict'; + +const test = require('supertape'); +const removeExtension = require(`./remove-extension`); test('cloudcmd: client: modules: operation: removeExtension: .tar.gz', (t) => { const name = 'hello'; diff --git a/client/modules/operation/set-listeners.js b/client/modules/operation/set-listeners.mjs similarity index 86% rename from client/modules/operation/set-listeners.js rename to client/modules/operation/set-listeners.mjs index e54e7d14..d5052cff 100644 --- a/client/modules/operation/set-listeners.js +++ b/client/modules/operation/set-listeners.mjs @@ -1,7 +1,7 @@ /* global DOM */ import forEachKey from 'for-each-key'; import wraptile from 'wraptile'; -import {format} from './format.js'; +import format from './format.js'; const {Dialog, Images} = DOM; @@ -15,12 +15,15 @@ export const setListeners = (options) => (emitter) => { } = options; let done; + let lastError; const onAbort = wraptile(({emitter, operation}) => { emitter.abort(); const msg = `${operation} aborted`; + lastError = true; + Dialog.alert(msg, { cancel: false, }); @@ -37,10 +40,13 @@ export const setListeners = (options) => (emitter) => { operation, })); + let noProgress = true; + const listeners = { progress: (value) => { done = value === 100; progress.setProgress(value); + noProgress = false; }, end: () => { @@ -48,10 +54,13 @@ export const setListeners = (options) => (emitter) => { forEachKey(removeListener, listeners); progress.remove(); - callback(); + if (lastError || done || noProgress) + callback(); }, error: async (error) => { + lastError = error; + if (noContinue) { listeners.end(error); Dialog.alert(error); diff --git a/client/modules/polyfill.js b/client/modules/polyfill.js index 90b62689..ff0e59c5 100644 --- a/client/modules/polyfill.js +++ b/client/modules/polyfill.js @@ -1,8 +1,12 @@ -import _scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'; +'use strict'; + +require('domtokenlist-shim'); + +const _scrollIntoViewIfNeeded = require('scroll-into-view-if-needed'); globalThis.DOM = globalThis.DOM || {}; -export const scrollIntoViewIfNeeded = (el, overrides = {}) => { +const scrollIntoViewIfNeeded = (el, overrides = {}) => { const { scroll = _scrollIntoViewIfNeeded, } = overrides; @@ -13,3 +17,4 @@ export const scrollIntoViewIfNeeded = (el, overrides = {}) => { }; globalThis.DOM.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded; +module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded; diff --git a/client/modules/polyfill.spec.js b/client/modules/polyfill.spec.js index 740947eb..79d9dfda 100644 --- a/client/modules/polyfill.spec.js +++ b/client/modules/polyfill.spec.js @@ -1,7 +1,9 @@ -import {test, stub} from 'supertape'; -import {scrollIntoViewIfNeeded} from './polyfill.js'; +'use strict'; -test('cloudcmd: client: polyfill: scrollIntoViewIfNeeded', (t) => { +const {test, stub} = require('supertape'); +const {scrollIntoViewIfNeeded} = require('./polyfill'); + +test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => { const scroll = stub(); const el = {}; @@ -15,6 +17,6 @@ test('cloudcmd: client: polyfill: scrollIntoViewIfNeeded', (t) => { }, ]; - t.calledWith(scroll, args, 'should call scrollIntoViewIfNeeded'); + t.calledWith(scroll, args, 'should call scrollIntoViewIfNeaded'); t.end(); }); diff --git a/client/modules/terminal-run.js b/client/modules/terminal-run.js index 029415ea..4dcf24ac 100644 --- a/client/modules/terminal-run.js +++ b/client/modules/terminal-run.js @@ -1,21 +1,21 @@ -import '../../css/terminal.css'; -import {promisify} from 'es6-promisify'; -import {tryToCatch} from 'try-to-catch'; -import {fullstore} from 'fullstore'; -import exec from 'execon'; -import load from 'load.js'; -import DOM from '#dom'; -import * as Images from '#dom/images'; +'use strict'; + +/* global CloudCmd, gritty */ +const {promisify} = require('es6-promisify'); +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 {Dialog} = DOM; -const {CloudCmd} = globalThis; const {Key, config} = CloudCmd; -CloudCmd.TerminalRun = { - init, - show, - hide, -}; +CloudCmd.TerminalRun = exports; let Loaded; let Terminal; @@ -40,7 +40,7 @@ const loadAll = async () => { Loaded = true; }; -export async function init() { +module.exports.init = async () => { if (!config('terminal')) return; @@ -48,13 +48,9 @@ export async function init() { await CloudCmd.View(); await loadAll(); -} +}; -export async function show(options = {}) { - return await runTerminal(options); -} - -const runTerminal = promisify((options, fn) => { +module.exports.show = promisify((options = {}, fn) => { if (!Loaded) return fn(null, -1); @@ -73,7 +69,9 @@ const runTerminal = promisify((options, fn) => { }); }); -export function hide() { +module.exports.hide = hide; + +function hide() { CloudCmd.View.hide(); } @@ -110,7 +108,7 @@ function create(createOptions) { let commandExit = false; - const {socket, terminal} = globalThis.gritty(document.body, options); + const {socket, terminal} = gritty(document.body, options); Socket = socket; Terminal = terminal; diff --git a/client/modules/terminal.js b/client/modules/terminal.js index 186ee0b2..6a355560 100644 --- a/client/modules/terminal.js +++ b/client/modules/terminal.js @@ -1,22 +1,22 @@ -import '#css/terminal.css'; -import {tryToCatch} from 'try-to-catch'; -import exec from 'execon'; -import load from 'load.js'; -import * as Images from '#dom/images'; -import DOM from '#dom'; +'use strict'; + +/* global CloudCmd */ +/* global gritty */ +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 loadParallel = load.parallel; -const {CloudCmd} = globalThis; - const {Dialog} = DOM; -const {Key, config} = globalThis.CloudCmd; +const {Key, config} = CloudCmd; -CloudCmd.Terminal = { - init, - show, - hide, -}; +CloudCmd.Terminal = exports; let Loaded; let Terminal; @@ -39,7 +39,7 @@ const loadAll = async () => { Loaded = true; }; -export async function init() { +module.exports.init = async () => { if (!config('terminal')) return; @@ -48,9 +48,12 @@ export async function init() { await CloudCmd.View(); await loadAll(); create(); -} +}; -export function hide() { +module.exports.show = show; +module.exports.hide = hide; + +function hide() { CloudCmd.View.hide(); } @@ -75,7 +78,7 @@ function create() { fontFamily: 'Droid Sans Mono', }; - const {socket, terminal} = globalThis.gritty(document.body, options); + const {socket, terminal} = gritty(document.body, options); Socket = socket; Terminal = terminal; @@ -98,7 +101,7 @@ function authCheck(spawn) { }); } -export function show() { +function show() { if (!Loaded) return; diff --git a/client/modules/upload.js b/client/modules/upload.js index 8433fbf5..23fbdaf5 100644 --- a/client/modules/upload.js +++ b/client/modules/upload.js @@ -1,21 +1,23 @@ /* global CloudCmd, DOM */ -import createElement from '@cloudcmd/create-element'; -import * as Files from '#dom/files'; -import {uploadFiles} from '#dom/upload-files'; -import * as Images from '#dom/images'; -CloudCmd.Upload = { - init, - show, - hide, -}; +'use strict'; -export async function init() { +CloudCmd.Upload = exports; + +const createElement = require('@cloudcmd/create-element'); +const Files = require('../dom/files'); +const Images = require('../dom/images.mjs'); +const uploadFiles = require('../dom/upload-files'); + +module.exports.init = async () => { Images.show.load('top'); await CloudCmd.View(); -} +}; -export async function show() { +module.exports.show = show; +module.exports.hide = hide; + +async function show() { Images.show.load('top'); const innerHTML = await Files.get('upload'); @@ -47,7 +49,7 @@ export async function show() { }); } -export function hide() { +function hide() { CloudCmd.View.hide(); } diff --git a/client/modules/user-menu/get-user-menu.js b/client/modules/user-menu/get-user-menu.js index aa3a1076..6281eda1 100644 --- a/client/modules/user-menu/get-user-menu.js +++ b/client/modules/user-menu/get-user-menu.js @@ -1,4 +1,6 @@ -export const getUserMenu = (menuFn) => { +'use strict'; + +module.exports = (menuFn) => { const module = {}; const fn = Function('module', menuFn); diff --git a/client/modules/user-menu/get-user-menu.spec.js b/client/modules/user-menu/get-user-menu.spec.js index 606eb820..20f2e11e 100644 --- a/client/modules/user-menu/get-user-menu.spec.js +++ b/client/modules/user-menu/get-user-menu.spec.js @@ -1,5 +1,7 @@ -import test from 'supertape'; -import {getUserMenu} from './get-user-menu.js'; +'use strict'; + +const test = require('supertape'); +const getUserMenu = require('./get-user-menu'); test('user-menu: getUserMenu', (t) => { const menu = `module.exports = { diff --git a/client/modules/user-menu/index.js b/client/modules/user-menu/index.js index 60d32d95..1dba5d23 100644 --- a/client/modules/user-menu/index.js +++ b/client/modules/user-menu/index.js @@ -1,49 +1,47 @@ -import '../../../css/user-menu.css'; -import currify from 'currify'; -import wraptile from 'wraptile'; -import {fullstore} from 'fullstore'; -import load from 'load.js'; -import createElement from '@cloudcmd/create-element'; -import {tryCatch} from 'try-catch'; -import {tryToCatch} from 'try-to-catch'; -import {codeFrameColumns} from '@babel/code-frame'; -import * as Dialog from '#dom/dialog'; -import * as Images from '#dom/images'; -import {getUserMenu} from './get-user-menu.js'; -import {navigate} from './navigate.js'; -import {parseError} from './parse-error.js'; -import {parseUserMenu} from './parse-user-menu.js'; -import {runSelected} from './run.js'; +'use strict'; + +/* global CloudCmd, DOM */ +require('../../../css/user-menu.css'); + +const currify = require('currify'); +const wraptile = require('wraptile'); +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 {codeFrameColumns} = require('@babel/code-frame'); + +const Images = require('../../dom/images.mjs'); +const Dialog = require('../../dom/dialog'); +const getUserMenu = require('./get-user-menu'); +const navigate = require('./navigate'); +const parseError = require('./parse-error'); +const parseUserMenu = require('./parse-user-menu'); +const {runSelected} = require('./run'); const loadCSS = load.css; const sourceStore = fullstore(); -const { - CloudCmd, - DOM, - CloudFunc, -} = globalThis; - const Name = 'UserMenu'; -CloudCmd[Name] = { - init, - show, - hide, -}; +CloudCmd[Name] = module.exports; const {Key} = CloudCmd; -export async function init() { +module.exports.init = async () => { await Promise.all([ loadCSS(`${CloudCmd.prefix}/dist/user-menu.css`), CloudCmd.View(), ]); -} +}; + +module.exports.show = show; +module.exports.hide = hide; const {CurrentInfo} = DOM; -export async function show() { +async function show() { Images.show.load('top'); const {dirPath} = CurrentInfo; @@ -109,7 +107,7 @@ function fillTemplate(options) { return result.join(''); } -export function hide() { +function hide() { CloudCmd.View.hide(); } @@ -151,7 +149,6 @@ const runUserMenu = async (fn) => { const [error] = await tryToCatch(fn, { DOM, CloudCmd, - CloudFunc, tryToCatch, }); diff --git a/client/modules/user-menu/navigate.js b/client/modules/user-menu/navigate.js index 0fa4d672..89334ebc 100644 --- a/client/modules/user-menu/navigate.js +++ b/client/modules/user-menu/navigate.js @@ -1,15 +1,18 @@ -import {fullstore} from 'fullstore'; -import { +'use strict'; + +const {fullstore} = require('fullstore'); + +const { J, K, UP, DOWN, -} from '../../key/key.js'; +} = require('../../key/key.mjs'); const store = fullstore(1); const isDigit = (a) => /^\d+$/.test(a); -export const navigate = (el, {key, keyCode}) => { +module.exports = (el, {key, keyCode}) => { if (isDigit(key)) store(Number(key)); diff --git a/client/modules/user-menu/navigate.spec.js b/client/modules/user-menu/navigate.spec.js index 6539c8e9..6fe97d15 100644 --- a/client/modules/user-menu/navigate.spec.js +++ b/client/modules/user-menu/navigate.spec.js @@ -1,11 +1,14 @@ -import test from 'supertape'; -import {navigate} from './navigate.js'; -import { +'use strict'; + +const test = require('supertape'); +const navigate = require('./navigate'); + +const { UP, DOWN, J, K, -} from '../../key/key.js'; +} = require('../../key/key.mjs'); test('cloudcmd: user-menu: navigate: DOWN', (t) => { const el = { diff --git a/client/modules/user-menu/parse-error.js b/client/modules/user-menu/parse-error.js index 23d605e0..aa7d574a 100644 --- a/client/modules/user-menu/parse-error.js +++ b/client/modules/user-menu/parse-error.js @@ -1,6 +1,8 @@ +'use strict'; + const isNumber = (a) => typeof a === 'number'; -export const parseError = (error) => { +module.exports = (error) => { const {lineNumber, columnNumber} = error; // thank you firefox diff --git a/client/modules/user-menu/parse-error.spec.js b/client/modules/user-menu/parse-error.spec.js index 5ec8da45..cb78fc6d 100644 --- a/client/modules/user-menu/parse-error.spec.js +++ b/client/modules/user-menu/parse-error.spec.js @@ -1,5 +1,7 @@ -import test from 'supertape'; -import {parseError} from './parse-error.js'; +'use strict'; + +const test = require('supertape'); +const parseError = require('./parse-error'); test('user-menu: parse-error', (t) => { const result = parseError({ diff --git a/client/modules/user-menu/parse-user-menu.js b/client/modules/user-menu/parse-user-menu.js index e4ad39e3..f97cc109 100644 --- a/client/modules/user-menu/parse-user-menu.js +++ b/client/modules/user-menu/parse-user-menu.js @@ -1,6 +1,8 @@ +'use strict'; + const {entries, assign} = Object; -export const parseUserMenu = (userMenu) => { +module.exports = (userMenu) => { const names = []; const keys = {}; const items = {}; diff --git a/client/modules/user-menu/parse-user-menu.spec.js b/client/modules/user-menu/parse-user-menu.spec.js index fb763209..0807896f 100644 --- a/client/modules/user-menu/parse-user-menu.spec.js +++ b/client/modules/user-menu/parse-user-menu.spec.js @@ -1,11 +1,13 @@ -import {test, stub} from 'supertape'; -import {parseUserMenu} from './parse-user-menu.js'; +'use strict'; + +const {test, stub} = require('supertape'); + +const parse = require('./parse-user-menu'); test('cloudcmd: user menu: parse', (t) => { const fn = stub(); const __settings = {}; - - const result = parseUserMenu({ + const result = parse({ __settings, 'F2 - Rename file': fn, '_f': fn, diff --git a/client/modules/user-menu/run.js b/client/modules/user-menu/run.js index 51e9fe02..61228be9 100644 --- a/client/modules/user-menu/run.js +++ b/client/modules/user-menu/run.js @@ -1,4 +1,6 @@ -export const runSelected = async (selectedItems, items, runUserMenu) => { +'use strict'; + +module.exports.runSelected = async (selectedItems, items, runUserMenu) => { for (const selected of selectedItems) { await runUserMenu(items[selected]); } diff --git a/client/modules/user-menu/run.spec.js b/client/modules/user-menu/run.spec.js index 86738712..488c6c27 100644 --- a/client/modules/user-menu/run.spec.js +++ b/client/modules/user-menu/run.spec.js @@ -1,5 +1,8 @@ -import {test, stub} from 'supertape'; -import {runSelected} from './run.js'; +'use strict'; + +const {test, stub} = require('supertape'); + +const {runSelected} = require('./run'); test('cloudcmd: client: user menu: run', async (t) => { const runUserMenu = stub(); diff --git a/client/modules/view/get-type.js b/client/modules/view/get-type.js index a061399f..9fc1df1d 100644 --- a/client/modules/view/get-type.js +++ b/client/modules/view/get-type.js @@ -1,5 +1,6 @@ -import currify from 'currify'; +'use strict'; +const currify = require('currify'); const testRegExp = currify((name, reg) => reg.test(name)); const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i'); @@ -7,7 +8,7 @@ const isPDF = (a) => /\.pdf$/i.test(a); const isHTML = (a) => a.endsWith('.html'); const isMarkdown = (a) => /.\.md$/.test(a); -export default (name) => { +module.exports = (name) => { if (isPDF(name)) return 'pdf'; diff --git a/client/modules/view/index.js b/client/modules/view/index.js index 6626f1aa..89d68ee4 100644 --- a/client/modules/view/index.js +++ b/client/modules/view/index.js @@ -1,25 +1,35 @@ -import '#css/view.css'; -import {rendy} from 'rendy'; -import currify from 'currify'; -import wraptile from 'wraptile'; -import {tryToCatch} from 'try-to-catch'; -import load from 'load.js'; -import * as _modal from '@cloudcmd/modal'; -import _createElement from '@cloudcmd/create-element'; -import {time} from '#common/util'; -import * as Files from '#dom/files'; -import * as Events from '#dom/events'; -import {FS} from '#common/cloudfunc'; -import * as Images from '#dom/images'; -import {encode} from '#common/entity'; -import { - isImage, - isAudio, - getType, -} from './types.js'; +/* global CloudCmd, DOM */ + +'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 load = require('load.js'); + +const _modal = require('@cloudcmd/modal'); +const _createElement = require('@cloudcmd/create-element'); + +const {time} = require('../../../common/util'); +const {FS} = require('../../../common/cloudfunc.mjs'); + +const { + isImage, + isAudio, + getType, +} = require('./types'); + +const Files = require('../../dom/files'); +const Events = require('#dom/events'); +const Images = require('../../dom/images.mjs'); + +const {encode} = require('../../../common/entity'); const isString = (a) => typeof a === 'string'; const {assign} = Object; const {isArray} = Array; @@ -37,15 +47,14 @@ const addEvent = lifo(Events.add); const loadCSS = load.css; +module.exports.show = show; +module.exports.hide = hide; + let Loading = false; const Name = 'View'; -CloudCmd[Name] = { - init, - show, - hide, -}; +CloudCmd[Name] = module.exports; const Info = DOM.CurrentInfo; const {Key} = CloudCmd; @@ -82,9 +91,9 @@ const Config = { }, }; -export const _Config = Config; +module.exports._Config = Config; -export async function init() { +module.exports.init = async () => { await loadAll(); const events = [ @@ -96,9 +105,9 @@ export async function init() { Overlay, onOverlayClick, )); -} +}; -export async function show(data, options = {}) { +async function show(data, options = {}) { const prefixURL = CloudCmd.prefixURL + FS; if (Loading) @@ -150,8 +159,7 @@ export async function show(data, options = {}) { } } -export const _createIframe = createIframe; - +module.exports._createIframe = createIframe; function createIframe(src, overrides = {}) { const { createElement = _createElement, @@ -170,8 +178,7 @@ function createIframe(src, overrides = {}) { return element; } -export const _viewHtml = viewHtml; - +module.exports._viewHtml = viewHtml; function viewHtml(src, overrides = {}) { const {modal = _modal} = overrides; modal.open(createIframe(src), Config); @@ -227,8 +234,7 @@ async function viewFile() { const copy = (a) => assign({}, a); -export const _initConfig = initConfig; - +module.exports._initConfig = initConfig; function initConfig(options) { const config = copy(Config); @@ -254,7 +260,7 @@ function initConfig(options) { return config; } -export function hide() { +function hide() { _modal.close(); } diff --git a/client/modules/view/index.spec.js b/client/modules/view/index.spec.js index abfbe3f4..cc46d07d 100644 --- a/client/modules/view/index.spec.js +++ b/client/modules/view/index.spec.js @@ -1,14 +1,17 @@ -import autoGlobals from 'auto-globals'; -import {stub} from '@cloudcmd/stub'; -import {test as tape} from 'supertape'; -import { +'use strict'; + +require('css-modules-require-hook/preset'); + +const autoGlobals = require('auto-globals'); +const {stub} = require('@cloudcmd/stub'); + +const test = autoGlobals(require('supertape')); +const { _initConfig, _viewHtml, _Config, _createIframe, -} from './index.js'; - -const test = autoGlobals(tape); +} = require('.'); test('cloudcmd: client: view: initConfig', (t) => { let config; @@ -31,10 +34,8 @@ test('cloudcmd: client: view: initConfig', (t) => { test('cloudcmd: client: view: initConfig: no options', (t) => { const config = _initConfig(); - const result = typeof config; - const expected = 'object'; - t.equal(result, expected); + t.equal(typeof config, 'object'); t.end(); }); diff --git a/client/modules/view/types.js b/client/modules/view/types.js index 320a4bc8..d94dd971 100644 --- a/client/modules/view/types.js +++ b/client/modules/view/types.js @@ -1,8 +1,8 @@ -import {extname} from 'node:path'; -import currify from 'currify'; - -export const isAudio = (name) => /\.(mp3|ogg|m4a|flac)$/i.test(name); +'use strict'; +const {extname} = require('node:path'); +const currify = require('currify'); +const isAudio = (name) => /\.(mp3|ogg|m4a)$/i.test(name); const testRegExp = currify((name, reg) => reg.test(name)); const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i'); @@ -10,7 +10,7 @@ const isPDF = (a) => /\.pdf$/i.test(a); const isHTML = (a) => a.endsWith('.html'); const isMarkdown = (a) => /.\.md$/.test(a); -export const getType = async (path) => { +module.exports.getType = async (path) => { const ext = extname(path); if (!ext) @@ -32,7 +32,8 @@ export const getType = async (path) => { return 'markdown'; }; -export function isImage(name) { +module.exports.isImage = isImage; +function isImage(name) { const images = [ 'jp(e|g|eg)', 'gif', @@ -52,12 +53,13 @@ function isMedia(name) { return isAudio(name) || isVideo(name); } +module.exports.isAudio = isAudio; + function isVideo(name) { return /\.(mp4|avi|webm)$/i.test(name); } -export const _detectType = detectType; - +module.exports._detectType = detectType; async function detectType(path) { const {headers} = await fetch(path, { method: 'HEAD', diff --git a/client/modules/view/types.spec.js b/client/modules/view/types.spec.js index f0542c18..d8004f9e 100644 --- a/client/modules/view/types.spec.js +++ b/client/modules/view/types.spec.js @@ -1,5 +1,7 @@ -import {test, stub} from 'supertape'; -import {isAudio, _detectType} from './types.js'; +'use strict'; + +const {test, stub} = require('supertape'); +const {isAudio, _detectType} = require('./types'); test('cloudcmd: client: view: types: isAudio', (t) => { const result = isAudio('hello.mp3'); @@ -8,13 +10,6 @@ test('cloudcmd: client: view: types: isAudio', (t) => { t.end(); }); -test('cloudcmd: client: view: types: isAudio: flac', (t) => { - const result = isAudio('hello.flac'); - - t.ok(result); - t.end(); -}); - test('cloudcmd: client: view: types: isAudio: no', (t) => { const result = isAudio('hello'); diff --git a/client/sort.js b/client/sort.mjs similarity index 96% rename from client/sort.js rename to client/sort.mjs index ed0971f3..73539c81 100644 --- a/client/sort.js +++ b/client/sort.mjs @@ -1,6 +1,6 @@ /* global CloudCmd */ import {fullstore} from 'fullstore'; -import DOM from '#dom'; +import DOM from './dom/index.js'; const sortPrevious = fullstore(); diff --git a/client/sw/register.js b/client/sw/register.mjs similarity index 90% rename from client/sw/register.js rename to client/sw/register.mjs index 928d63c9..da3ff0f2 100644 --- a/client/sw/register.js +++ b/client/sw/register.mjs @@ -16,7 +16,7 @@ export async function registerSW(prefix) { const {serviceWorker} = navigator; const register = serviceWorker.register.bind(serviceWorker); - const [e, sw] = await tryToCatch(register, `${prefix}/sw.js`); + const [e, sw] = await tryToCatch(register, `${prefix}/sw.mjs`); if (e) return null; diff --git a/client/sw/register.spec.js b/client/sw/register.spec.mjs similarity index 92% rename from client/sw/register.spec.js rename to client/sw/register.spec.mjs index 52699f0b..5734a130 100644 --- a/client/sw/register.spec.js +++ b/client/sw/register.spec.mjs @@ -6,7 +6,7 @@ import { listenSW, registerSW, unregisterSW, -} from './register.js'; +} from './register.mjs'; const test = autoGlobals(tape); @@ -65,7 +65,7 @@ test('sw: register: registerSW: http', async (t, {location, navigator}) => { test('sw: register: registerSW: https self-signed', async (t, {location, navigator}) => { Object.assign(location, { - protocol: 'https:', + protocol: 'https', hostname: 'self-signed.badssl.com', }); @@ -84,7 +84,7 @@ test('sw: register: registerSW', async (t, {location, navigator}) => { const {register} = navigator.serviceWorker; await registerSW('/hello'); - t.calledWith(register, ['/hello/sw.js'], 'should call register'); + t.calledWith(register, ['/hello/sw.mjs'], 'should call register'); t.end(); }); @@ -98,6 +98,6 @@ test('sw: register: unregisterSW', async (t, {location, navigator}) => { await unregisterSW('/hello'); - t.calledWith(register, ['/hello/sw.js'], 'should call register'); + t.calledWith(register, ['/hello/sw.mjs'], 'should call register'); t.end(); }); diff --git a/client/sw/sw.js b/client/sw/sw.mjs similarity index 99% rename from client/sw/sw.js rename to client/sw/sw.mjs index 5b5643a3..30b8909c 100644 --- a/client/sw/sw.js +++ b/client/sw/sw.mjs @@ -96,3 +96,4 @@ async function addToCache(request, response) { const cache = await caches.open(NAME); return cache.put(request, response); } + diff --git a/common/callbackify.js b/common/callbackify.js index 71160ac7..c2040a3d 100644 --- a/common/callbackify.js +++ b/common/callbackify.js @@ -1,6 +1,8 @@ +'use strict'; + const success = (f) => (data) => f(null, data); -export default (promise) => (...a) => { +module.exports = (promise) => (...a) => { const fn = a.pop(); promise(...a) diff --git a/common/callbackify.spec.js b/common/callbackify.spec.js index d1586d5a..0a46d9e9 100644 --- a/common/callbackify.spec.js +++ b/common/callbackify.spec.js @@ -1,7 +1,11 @@ -import {promisify} from 'node:util'; -import {tryToCatch} from 'try-to-catch'; -import {test, stub} from 'supertape'; -import callbackify from './callbackify.js'; +'use strict'; + +const {promisify} = require('node:util'); +const {tryToCatch} = require('try-to-catch'); + +const {test, stub} = require('supertape'); + +const callbackify = require('./callbackify'); test('cloudcmd: common: callbackify: error', async (t) => { const promise = stub().rejects(Error('hello')); diff --git a/common/cloudfunc.js b/common/cloudfunc.mjs similarity index 93% rename from common/cloudfunc.js rename to common/cloudfunc.mjs index 7884d069..00a48360 100644 --- a/common/cloudfunc.js +++ b/common/cloudfunc.mjs @@ -1,16 +1,17 @@ -import {rendy} from 'rendy'; +import rendy from 'rendy'; import currify from 'currify'; -import {fullstore} from 'fullstore'; -import {encode} from '#common/entity'; +import store from 'fullstore'; +import {encode} from './entity.js'; -const id = (a) => a; +export const getHeaderField = currify(_getHeaderField); + +/* КОНСТАНТЫ (общие для клиента и сервера)*/ +/* название программы */ const NAME = 'Cloud Commander'; -export const dateFormatter = fullstore(id); -export const getHeaderField = currify(_getHeaderField); export const FS = '/fs'; -const Path = fullstore(); +const Path = store(); Path('/'); @@ -122,13 +123,12 @@ export const buildFromJSON = (params) => { showDotFiles, } = params; - const formatDate = dateFormatter(); - const templateFile = template.file; const templateLink = template.link; const json = params.data; const path = encode(json.path); + const {files} = json; /* @@ -151,7 +151,6 @@ export const buildFromJSON = (params) => { const name = getFieldName('name'); const size = getFieldName('size'); const date = getFieldName('date'); - const time = getFieldName('time'); const header = rendy(templateFile, { tag: 'div', @@ -161,7 +160,6 @@ export const buildFromJSON = (params) => { name, size, date, - time, owner, mode, }); @@ -194,7 +192,6 @@ export const buildFromJSON = (params) => { name: linkResult, size: '<dir>', date: '--.--.----', - time: '--:--:--', owner: '.', mode: '--- --- ---', }); @@ -213,7 +210,6 @@ export const buildFromJSON = (params) => { type, mode, date, - time, owner, size, } = file; @@ -235,8 +231,7 @@ export const buildFromJSON = (params) => { type, name: linkResult, size, - date: formatDate(date), - time, + date, owner, mode, }); @@ -251,7 +246,6 @@ export const buildFromJSON = (params) => { const updateField = (file) => ({ ...file, date: file.date || '--.--.----', - time: file.time || '--:--:--', owner: file.owner || 'root', size: getSize(file), }); diff --git a/common/cloudfunc.spec.js b/common/cloudfunc.spec.js deleted file mode 100644 index b679feeb..00000000 --- a/common/cloudfunc.spec.js +++ /dev/null @@ -1,364 +0,0 @@ -import {readFileSync} from 'node:fs'; -import test from 'supertape'; -import {montag} from 'montag'; -import * as cheerio from 'cheerio'; -import {tryCatch} from 'try-catch'; -import { - _getSize, - getPathLink, - buildFromJSON, - _getDataName, - dateFormatter, - formatMsg, - getTitle, - getDotDot, - getHeaderField, -} from '#common/cloudfunc'; - -const templatePath = new URL('../tmpl/fs', import.meta.url).pathname; - -const template = { - pathLink: readFileSync(`${templatePath}/pathLink.hbs`, 'utf8'), - path: readFileSync(`${templatePath}/path.hbs`, 'utf8'), - file: readFileSync(`${templatePath}/file.hbs`, 'utf8'), - link: readFileSync(`${templatePath}/link.hbs`, 'utf8'), -}; - -test('cloudfunc: buildFromJSON: ..', (t) => { - const data = { - path: '/media/', - files: [{ - date: '30.08.2016', - mode: 'rwx rwx rwx', - name: 'floppy', - owner: 'root', - size: '7b', - type: 'directory-link', - }], - }; - - const html = buildFromJSON({ - prefix: '', - template, - data, - }); - - const $ = cheerio.load(html); - const el = $('[data-name="js-file-Li4="]'); - - const result = el - .find('[data-name="js-name"]') - .text(); - - const expected = '..'; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: getPathLink: /', (t) => { - const {pathLink} = template; - const result = getPathLink('/', '', pathLink); - - const expected = montag` - / - `; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: getPathLink: /hello/world', (t) => { - const {pathLink} = template; - const result = getPathLink('/hello/world', '', pathLink); - - const expected = montag` - /hello/ - `; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: getPathLink: prefix', (t) => { - const {pathLink} = template; - const result = getPathLink('/hello/world', '/cloudcmd', pathLink); - - const expected = montag` - /hello/ - `; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: getSize: dir', (t) => { - const type = 'directory'; - const size = 0; - - const result = _getSize({ - type, - size, - }); - - const expected = '<dir>'; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: getSize: link: dir', (t) => { - const type = 'directory-link'; - const size = 0; - - const result = _getSize({ - type, - size, - }); - - const expected = '<link>'; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: getSize: link: file', (t) => { - const type = 'file-link'; - const size = 0; - - const result = _getSize({ - type, - size, - }); - - const expected = '<link>'; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: getSize: file', (t) => { - const type = 'file'; - const size = '100.00kb'; - - const result = _getSize({ - type, - size, - }); - - const expected = '100.00kb'; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: buildFromJSON: showDotFiles: false', (t) => { - const data = { - path: '/media/', - files: [{ - date: '30.08.2016', - mode: 'rwx rwx rwx', - name: '.floppy', - owner: 'root', - size: '7b', - type: 'directory-link', - }], - }; - - const html = buildFromJSON({ - prefix: '', - template, - data, - showDotFiles: false, - }); - - const $ = cheerio.load(html); - const el = $('[data-name="js-file-LmZsb3BweQ=="]'); - - const result = el - .find('[data-name="js-name"]') - .text(); - - const expected = ''; - - t.equal(result, expected); - t.end(); -}); - -test('cloudfunc: buildFromJSON: name: {{ }}', (t) => { - const data = { - path: '/media/', - files: [{ - date: '30.08.2016', - mode: 'rwx rwx rwx', - name: '{{}}', - owner: 'root', - size: '7b', - type: 'file', - }], - }; - - const html = buildFromJSON({ - prefix: '', - template, - data, - showDotFiles: false, - }); - - const $ = cheerio.load(html); - const el = $('[data-name="js-file-JTdCJTdCJTdEJTdE"]'); - - const result = el - .find('[data-name="js-name"]') - .text(); - - const expected = '{{}}'; - - 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(); -}); - -test('cloudfunc: formatMsg: name', (t) => { - const result = formatMsg('hello', 'world'); - const expected = 'hello: ok("world")'; - - t.equal(result, expected, 'should format message with name'); - t.end(); -}); - -test('cloudfunc: formatMsg: no name', (t) => { - const result = formatMsg('hello'); - const expected = 'hello: ok'; - - t.equal(result, expected, 'should format message without name'); - t.end(); -}); - -test('cloudfunc: getTitle: no options', (t) => { - const result = getTitle(); - - t.ok(result, 'should return a title string even without options'); - t.end(); -}); - -test('cloudfunc: getTitle: with name', (t) => { - const result = getTitle({ - name: 'MyName', - }); - - t.match(result, 'MyName', 'should return title with name'); - t.end(); -}); - -test('cloudfunc: getHeaderField: sort not name', (t) => { - const result = getHeaderField('size', 'asc', 'name'); - - t.equal(result, 'name', 'should return plain name when sort does not match'); - t.end(); -}); - -test('cloudfunc: getHeaderField: sort name asc', (t) => { - const result = getHeaderField('name', 'asc', 'name'); - - t.equal(result, 'name', 'should return plain name when name asc'); - t.end(); -}); - -test('cloudfunc: getDotDot: root', (t) => { - const result = getDotDot('/'); - - t.equal(result, '/', 'should return / for root path'); - t.end(); -}); - -test('cloudfunc: getPathLink: /a/b/c', (t) => { - const {pathLink} = template; - const result = getPathLink('/a/b/c/', '', pathLink); - - t.ok(result, 'should build path link for 3-segment path'); - t.end(); -}); - -test('cloudfunc: getPathLink: no url', (t) => { - const [error] = tryCatch(getPathLink); - - t.equal(error.message, 'url could not be empty!', 'should throw when url is empty'); - t.end(); -}); - -test('cloudfunc: getPathLink: no template', (t) => { - const [error] = tryCatch(getPathLink, '/'); - - t.equal(error.message, 'template could not be empty!', 'should throw when template is empty'); - t.end(); -}); - -test('cloudfunc: getHeaderField: sort name desc', (t) => { - const result = getHeaderField('name', 'desc', 'name'); - const expected = 'name↓'; - - t.equal(result, expected, 'should return name with down arrow'); - t.end(); -}); - -test('cloudfunc: getDotDot: normal path', (t) => { - const result = getDotDot('/hello/world/'); - const expected = '/hello'; - - t.equal(result, expected, 'should return parent directory'); - t.end(); -}); - -test('cloudfunc: buildFromJSON: formatDate', (t) => { - const data = { - path: '/media/', - files: [{ - date: '30.08.2016', - mode: 'rwx rwx rwx', - name: '{{}}', - owner: 'root', - size: '7b', - type: 'file', - }], - }; - - const oldFormatter = dateFormatter(); - - const formatDate = (str) => { - const [day, month, year] = str.split('.'); - const date = new Date(year, month - 1, day); - - return date.toLocaleDateString('en-US'); - }; - - dateFormatter(formatDate); - - const html = buildFromJSON({ - prefix: '', - template, - data, - showDotFiles: false, - }); - - dateFormatter(oldFormatter); - - const $ = cheerio.load(html); - const el = $('[data-name="js-file-JTdCJTdCJTdEJTdE"]'); - - const result = el - .find('[data-name="js-date"]') - .text(); - - const expected = '8/30/2016'; - - t.equal(result, expected); - t.end(); -}); diff --git a/common/cloudfunc.spec.mjs b/common/cloudfunc.spec.mjs new file mode 100644 index 00000000..79ddfedf --- /dev/null +++ b/common/cloudfunc.spec.mjs @@ -0,0 +1,181 @@ +import {readFileSync} from 'node:fs'; +import test from 'supertape'; +import montag from 'montag'; +import * as cheerio from 'cheerio'; +import { + _getSize, + getPathLink, + buildFromJSON, + _getDataName, +} from './cloudfunc.mjs'; + +const templatePath = new URL('../tmpl/fs', import.meta.url).pathname; + +const template = { + pathLink: readFileSync(`${templatePath}/pathLink.hbs`, 'utf8'), + path: readFileSync(`${templatePath}/path.hbs`, 'utf8'), + file: readFileSync(`${templatePath}/file.hbs`, 'utf8'), + link: readFileSync(`${templatePath}/link.hbs`, 'utf8'), +}; + +test('cloudfunc: buildFromJSON: ..', (t) => { + const data = { + path: '/media/', + files: [{ + date: '30.08.2016', + mode: 'rwx rwx rwx', + name: 'floppy', + owner: 'root', + size: '7b', + type: 'directory-link', + }], + }; + + const html = buildFromJSON({ + prefix: '', + template, + data, + }); + + const $ = cheerio.load(html); + const el = $('[data-name="js-file-Li4="]'); + + const result = el + .find('[data-name="js-name"]') + .text(); + + const expected = '..'; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: getPathLink: /', (t) => { + const {pathLink} = template; + const result = getPathLink('/', '', pathLink); + const expected = montag` + / + `; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: getPathLink: /hello/world', (t) => { + const {pathLink} = template; + const result = getPathLink('/hello/world', '', pathLink); + const expected = montag` + /hello/ + `; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: getPathLink: prefix', (t) => { + const {pathLink} = template; + const result = getPathLink('/hello/world', '/cloudcmd', pathLink); + const expected = montag` + /hello/ + `; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: getSize: dir', (t) => { + const type = 'directory'; + const size = 0; + const result = _getSize({ + type, + size, + }); + + const expected = '<dir>'; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: getSize: link: dir', (t) => { + const type = 'directory-link'; + const size = 0; + const result = _getSize({ + type, + size, + }); + + const expected = '<link>'; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: getSize: link: file', (t) => { + const type = 'file-link'; + const size = 0; + const result = _getSize({ + type, + size, + }); + + const expected = '<link>'; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: getSize: file', (t) => { + const type = 'file'; + const size = '100.00kb'; + const result = _getSize({ + type, + size, + }); + + const expected = '100.00kb'; + + t.equal(result, expected); + t.end(); +}); + +test('cloudfunc: buildFromJSON: showDotFiles: false', (t) => { + const data = { + path: '/media/', + files: [{ + date: '30.08.2016', + mode: 'rwx rwx rwx', + name: '.floppy', + owner: 'root', + size: '7b', + type: 'directory-link', + }], + }; + + const html = buildFromJSON({ + prefix: '', + template, + data, + showDotFiles: false, + }); + + const $ = cheerio.load(html); + const el = $('[data-name="js-file-LmZsb3BweQ=="]'); + + const result = el + .find('[data-name="js-name"]') + .text(); + + const expected = ''; + + 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.js b/common/datetime.js index d7982ab4..131c27fb 100644 --- a/common/datetime.js +++ b/common/datetime.js @@ -1,6 +1,8 @@ -import shortdate from 'shortdate'; +'use strict'; -export default (date) => { +const shortdate = require('shortdate'); + +module.exports = (date) => { date = date || new Date(); check(date); diff --git a/common/datetime.spec.js b/common/datetime.spec.js index e3edb2ef..cc321d8a 100644 --- a/common/datetime.spec.js +++ b/common/datetime.spec.js @@ -1,6 +1,9 @@ -import {test} from 'supertape'; -import {tryCatch} from 'try-catch'; -import datetime from './datetime.js'; +'use strict'; + +const test = require('supertape'); +const {tryCatch} = require('try-catch'); + +const datetime = require('./datetime'); test('common: datetime', (t) => { const dateStr = 'Fri, 17 Aug 2018 10:56:48'; diff --git a/common/entity.js b/common/entity.js index 690bbd7a..d6eeaa23 100644 --- a/common/entity.js +++ b/common/entity.js @@ -1,14 +1,15 @@ +'use strict'; + const Entities = { + // ' ': ' ', '<': '<', '>': '>', '"': '"', - '{': '{', - '}': '}', }; const keys = Object.keys(Entities); -export const encode = (str) => { +module.exports.encode = (str) => { for (const code of keys) { const char = Entities[code]; const reg = RegExp(char, 'g'); @@ -19,7 +20,7 @@ export const encode = (str) => { return str; }; -export const decode = (str) => { +module.exports.decode = (str) => { for (const code of keys) { const char = Entities[code]; const reg = RegExp(code, 'g'); diff --git a/common/entity.spec.js b/common/entity.spec.js index 79313f7c..e83b75df 100644 --- a/common/entity.spec.js +++ b/common/entity.spec.js @@ -1,5 +1,7 @@ -import {test} from 'supertape'; -import * as entity from '#common/entity'; +'use strict'; + +const test = require('supertape'); +const entity = require('./entity'); test('cloudcmd: entity: encode', (t) => { const result = entity.encode(' '); @@ -9,14 +11,6 @@ test('cloudcmd: entity: encode', (t) => { t.end(); }); -test('cloudcmd: entity: {{}}', (t) => { - const result = entity.encode('{{}}'); - const expected = '{{}}'; - - t.equal(result, expected, 'should encode entity'); - t.end(); -}); - test('cloudcmd: entity: decode', (t) => { const result = entity.decode('<hello> '); const expected = ' '; diff --git a/common/omit.js b/common/omit.js deleted file mode 100644 index 284c2f5b..00000000 --- a/common/omit.js +++ /dev/null @@ -1,12 +0,0 @@ -const difference = (a, b) => new Set(a).difference(new Set(b)); -const {keys} = Object; - -export const omit = (a, list) => { - const result = {}; - - for (const key of difference(keys(a), list)) { - result[key] = a[key]; - } - - return result; -}; diff --git a/common/omit.spec.js b/common/omit.spec.js deleted file mode 100644 index ff70541c..00000000 --- a/common/omit.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -import {test} from 'supertape'; -import {omit} from '#common/omit'; - -test('cloudcmd: common: omit', (t) => { - const a = { - hello: 1, - world: 2, - }; - - const result = omit(a, ['world']); - - const expected = { - hello: 1, - }; - - t.deepEqual(result, expected); - t.end(); -}); diff --git a/common/try-to-promise-all.js b/common/try-to-promise-all.js index 1dcfa3a8..e839d27e 100644 --- a/common/try-to-promise-all.js +++ b/common/try-to-promise-all.js @@ -1,8 +1,9 @@ -import {tryToCatch} from 'try-to-catch'; +'use strict'; +const {tryToCatch} = require('try-to-catch'); const all = Promise.all.bind(Promise); -export default async (a) => { +module.exports = async (a) => { const [e, result = []] = await tryToCatch(all, a); return [ diff --git a/common/try-to-promise-all.spec.js b/common/try-to-promise-all.spec.js index eafb036c..6a7e3b81 100644 --- a/common/try-to-promise-all.spec.js +++ b/common/try-to-promise-all.spec.js @@ -1,5 +1,7 @@ -import {test} from 'supertape'; -import tryToPromiseAll from './try-to-promise-all.js'; +'use strict'; + +const test = require('supertape'); +const tryToPromiseAll = require('./try-to-promise-all'); const resolve = Promise.resolve.bind(Promise); const reject = Promise.reject.bind(Promise); diff --git a/common/util.js b/common/util.js index 422b3c22..f3e00465 100644 --- a/common/util.js +++ b/common/util.js @@ -1,8 +1,9 @@ -import exec from 'execon'; +'use strict'; +const exec = require('execon'); const isString = (a) => typeof a === 'string'; -export const escapeRegExp = (str) => { +module.exports.escapeRegExp = (str) => { const isStr = isString(str); if (isStr) @@ -14,7 +15,7 @@ export const escapeRegExp = (str) => { /** * get regexp from wild card */ -export const getRegExp = (wildcard) => { +module.exports.getRegExp = (wildcard) => { const escaped = `^${wildcard // search from start of line .replace(/\./g, '\\.') .replace(/\*/g, '.*') @@ -24,13 +25,14 @@ export const getRegExp = (wildcard) => { return RegExp(escaped); }; +module.exports.exec = exec; /** * function gets file extension * * @param name * @return ext */ -export const getExt = (name) => { +module.exports.getExt = (name) => { const isStr = isString(name); if (!isStr) @@ -45,12 +47,12 @@ export const getExt = (name) => { }; /** - * find object by name in array + * find object by name in arrray * * @param array * @param name */ -export const findObjByNameInArr = (array, name) => { +module.exports.findObjByNameInArr = (array, name) => { let ret; if (!Array.isArray(array)) @@ -88,7 +90,7 @@ export const findObjByNameInArr = (array, name) => { * start timer * @param name */ -export const time = (name) => { +module.exports.time = (name) => { exec.ifExist(console, 'time', [name]); }; @@ -96,6 +98,6 @@ export const time = (name) => { * stop timer * @param name */ -export const timeEnd = (name) => { +module.exports.timeEnd = (name) => { exec.ifExist(console, 'timeEnd', [name]); }; diff --git a/common/util.spec.js b/common/util.spec.js index 5a19a8be..f62cb4ca 100644 --- a/common/util.spec.js +++ b/common/util.spec.js @@ -1,18 +1,19 @@ -import test from 'supertape'; -import {tryCatch} from 'try-catch'; -import { +'use strict'; + +const test = require('supertape'); +const {tryCatch} = require('try-catch'); +const Util = require('./util'); + +const { findObjByNameInArr, getRegExp, escapeRegExp, - getExt, - time, - timeEnd, -} from '#common/util'; +} = Util; test('getExt: no extension', (t) => { const EXT = ''; - const name = 'file-without-extension'; - const ext = getExt(name); + const name = 'file-withot-extension'; + const ext = Util.getExt(name); t.equal(ext, EXT, 'should return "" when extension is none'); t.end(); @@ -21,14 +22,14 @@ test('getExt: no extension', (t) => { test('getExt: return extension', (t) => { const EXT = '.png'; const name = 'picture.png'; - const ext = getExt(name); + const ext = Util.getExt(name); t.equal(ext, EXT, 'should return ".png" in files "picture.png"'); t.end(); }); test('util: getExt: no name', (t) => { - const ext = getExt(); + const ext = Util.getExt(); t.equal(ext, '', 'should return empty string'); t.end(); @@ -65,7 +66,6 @@ test('util: findObjByNameInArr: object', (t) => { test('util: findObjByNameInArr: array', (t) => { const name = 'hello'; const data = 'abc'; - const item = { name, data, @@ -87,9 +87,9 @@ test('util: findObjByNameInArr: array', (t) => { }); test('util: getRegExp', (t) => { - const reg = getRegExp('help?o.*'); + const reg = getRegExp('hel?o.*'); - t.deepEqual(reg, /^help.?o\..*$/, 'should return regexp'); + t.deepEqual(reg, /^hel.?o\..*$/, 'should return regexp'); t.end(); }); @@ -108,31 +108,11 @@ test('util: getRegExp: no', (t) => { }); test('util: escapeRegExp: no str', (t) => { - const result = escapeRegExp(1); - const expected = 1; - - t.equal(result, expected); + t.equal(escapeRegExp(1), 1); t.end(); }); test('util: escapeRegExp', (t) => { - const result = escapeRegExp('#hello'); - const expected = '\\#hello'; - - t.equal(result, expected); - t.end(); -}); - -test('util: time', (t) => { - const [error] = tryCatch(time, 'test'); - - t.notOk(error, 'should not throw'); - t.end(); -}); - -test('util: timeEnd', (t) => { - const [error] = tryCatch(timeEnd, 'test'); - - t.notOk(error, 'should not throw'); + t.equal(escapeRegExp('#hello'), '\\#hello'); t.end(); }); diff --git a/css/columns/name-size-date-time.css b/css/columns/name-size-date-time.css deleted file mode 100644 index f46e63f8..00000000 --- a/css/columns/name-size-date-time.css +++ /dev/null @@ -1,26 +0,0 @@ -.name { - width: 35%; -} - -.size { - float: none; -} - -.owner { - display: none; -} - -.mode { - display: none; -} - -.date { - float: right; - width: 19%; -} - -.time { - display: inline; - float: right; - width: 20%; -} diff --git a/css/columns/name-size-date.css b/css/columns/name-size-date.css index 9248f700..45c65c64 100644 --- a/css/columns/name-size-date.css +++ b/css/columns/name-size-date.css @@ -18,8 +18,3 @@ float: right; width: 19%; } - -.time { - display: none; -} - diff --git a/css/columns/name-size-time.css b/css/columns/name-size-time.css deleted file mode 100644 index 1bbd59de..00000000 --- a/css/columns/name-size-time.css +++ /dev/null @@ -1,24 +0,0 @@ -.name { - width: 55%; -} - -.size { - float: none; -} - -.owner { - display: none; -} - -.mode { - display: none; -} - -.date { - display: none; -} - -.time { - float: right; - width: 19%; -} diff --git a/css/columns/name-size.css b/css/columns/name-size.css index c02472a5..e58d1844 100644 --- a/css/columns/name-size.css +++ b/css/columns/name-size.css @@ -18,8 +18,3 @@ .date { display: none; } - -.time { - display: none; -} - diff --git a/css/query.css b/css/query.css index 509f1b45..b5843b85 100644 --- a/css/query.css +++ b/css/query.css @@ -161,9 +161,8 @@ .size, .date, .owner, - .time, .mode { - display: none !important; + display: none; } .name { diff --git a/css/style.css b/css/style.css index 10820444..cd7122f7 100644 --- a/css/style.css +++ b/css/style.css @@ -228,15 +228,9 @@ a:active { color: var(--column-color); } -.time { - color: var(--column-color); - display: none; -} - .owner { display: inline-block; - width: 12%; - + width: 13%; /* when inline-block * vertical align should be * set top to prevent additional @@ -248,7 +242,7 @@ a:active { .mode { float: right; - width: 22%; + width: 18%; color: var(--column-color); } diff --git a/cssnano.config.mjs b/cssnano.config.mjs new file mode 100644 index 00000000..44abaeaf --- /dev/null +++ b/cssnano.config.mjs @@ -0,0 +1,12 @@ +// used by OptimizeCssAssetsPlugin +import defaultPreset from 'cssnano-preset-default'; + +export default defaultPreset({ + svgo: { + plugins: [{ + convertPathData: false, + }, { + convertShapeToPath: false, + }], + }, +}); diff --git a/docker/Dockerfile b/docker/Dockerfile index 7c5d7b64..39312e00 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,22 +1,18 @@ -FROM node - +FROM node:lts-bookworm LABEL maintainer="Coderaiser" LABEL org.opencontainers.image.source="https://github.com/coderaiser/cloudcmd" -RUN mkdir -p /usr/src/cloudcmd +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app -WORKDIR /usr/src/cloudcmd - -COPY package.json /usr/src/cloudcmd/ +COPY package.json /usr/src/app/ RUN curl -fsSL https://bun.com/install | bash && \ - ln -s /root/.bun/bin/bun /usr/local/bin/bun && \ - chmod +x /usr/local/bin/bun && \ - bun r gritty --omit dev && \ - bun i gritty --omit dev && \ - bun pm cache rm + ~/.bun/bin/bun i --production --no-save && \ + PATH=$PATH:~/.bun/bin bun i gritty --no-save && \ + ~/.bun/bin/bun pm cache rm -COPY . /usr/src/cloudcmd +COPY . /usr/src/app WORKDIR / @@ -26,4 +22,4 @@ ENV cloudcmd_open=false EXPOSE 8000 -ENTRYPOINT ["/usr/src/cloudcmd/bin/cloudcmd.js"] +ENTRYPOINT ["/usr/src/app/bin/cloudcmd.mjs"] diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 5e38ab8a..b8cc6e3e 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -1,34 +1,30 @@ FROM node:alpine - LABEL maintainer="Coderaiser" LABEL org.opencontainers.image.source="https://github.com/coderaiser/cloudcmd" -RUN mkdir -p /usr/src/cloudcmd +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app -WORKDIR /usr/src/cloudcmd - -COPY package.json /usr/src/cloudcmd/ +COPY package.json /usr/src/app/ RUN apk update && \ apk add --no-cache curl bash make g++ python3 && \ - curl -fsSL https://bun.sh/install | bash && \ - ln -s ~/.bun/bin/bun /usr/local/bin/bun && \ - chmod +x /usr/local/bin/bun && \ - bun r gritty --omit dev && \ - bun i gritty --omit dev && \ - bun pm cache rm && \ + curl -fsSL https://bun.com/install | bash && \ + ~/.bun/bin/bun i --production --no-save && \ + PATH=$PATH:~/.bun/bin bun i gritty --no-save && \ + ~/.bun/bin/bun pm cache rm && \ apk del make g++ python3 && \ rm -rf /usr/include /tmp/* /var/cache/apk/* -COPY . /usr/src/cloudcmd +COPY . /usr/src/app WORKDIR / -ENV cloudcmd_terminal=true -ENV cloudcmd_terminal_path=gritty -ENV cloudcmd_open=false -ENV cloudcmd_vim=true +ENV cloudcmd_terminal true +ENV cloudcmd_terminal_path gritty +ENV cloudcmd_open false EXPOSE 8000 -ENTRYPOINT ["/usr/src/cloudcmd/bin/cloudcmd.js"] +ENTRYPOINT ["/usr/src/app/bin/cloudcmd.mjs"] + diff --git a/docker/Dockerfile.io b/docker/Dockerfile.io deleted file mode 100644 index d5a24459..00000000 --- a/docker/Dockerfile.io +++ /dev/null @@ -1,113 +0,0 @@ -FROM ubuntu:resolute - -LABEL maintainer="Coderaiser" -LABEL org.opencontainers.image.source="https://github.com/coderaiser/cloudcmd" - -RUN mkdir -p /usr/local/share/cloudcmd - -WORKDIR /usr/local/share/cloudcmd - -COPY package.json /usr/local/share/cloudcmd/ - -ENV DEBIAN_FRONTEND=noninteractive \ - NVM_DIR=/usr/local/share/nvm \ - npm_config_cache=/tmp/npm-cache \ - GOPATH=/usr/local/share/go \ - PATH=/usr/local/share/bun/bin:$PATH \ - BUN_INSTALL=/usr/local/share/bun \ - NPM_CONFIG_CACHE=/tmp/.npm \ - NPM_CONFIG_PREFIX=/usr/local \ - NPM_CONFIG_PACKAGE_LOCK=false \ - PALABRA_DIR=/usr/local/share \ - XDG_CONFIG_HOME=/usr/local/etc - -ARG UBUNTU_DEPS="libatomic1 curl wget git net-tools iproute2 software-properties-common" -ARG RUST_DEPS="build-essential" -ARG DEPS="pv gcc gdb strace upx-ucl less ffmpeg net-tools netcat-openbsd mc far2l iputils-ping vim bat fzf locales sudo command-not-found ncdu aptitude htop btop hexyl tmux" -ARG PALABRA_DEPS="nvm node rust go deno fasm nvchad rizin yara gdu f4 typos shellcheck gh" -ARG BUN_DEPS="palabra wisdom nupdate version-io redrun superc8 supertape madrun redlint putout renamify-cli runny redfork cline" - -RUN apt-get update && \ - apt-get upgrade -y && \ - apt-get autoremove && \ - apt-get install -y ${UBUNTU_DEPS} ${RUST_DEPES} ${DEPS} && \ - echo "> Install git" && \ - add-apt-repository ppa:git-core/ppa -y && \ - echo "> Update command-not-found database. Run 'sudo apt update' to populate it." && \ - apt-get update && \ - apt-get upgrade -y && \ - apt-get autoremove && \ - apt-get clean && \ - echo "> create user" && \ - useradd -m -s /bin/bash -u 1337 instalador && \ - chown -R instalador /usr/local && \ - chown -R instalador /tmp - -USER instalador - -RUN echo "> install bun" && \ - curl https://bun.sh/install | bash && \ - echo "> install npm globals" && \ - bun i ${BUN_DEPS} -g && \ - echo "> install rust go deno bun fasm nvim" && \ - bun ${BUN_INSTALL}/bin/palabra i ${PALABRA_DEPS} && \ - echo "> install node" && \ - . $NVM_DIR/nvm.sh - -USER root - -RUN echo "> remove user" && \ - userdel -r instalador && \ - echo "> install gritty" && \ - bun r gritty --omit dev && \ - bun i gritty --omit dev && \ - bun pm cache rm && \ - echo "> setup cloudcmd" && \ - ln -s /usr/local/share/cloudcmd/bin/cloudcmd.js /usr/local/bin/cloudcmd && \ - echo "> setup git" && \ - git config --global core.whitespace -trailing-space && \ - git config --global pull.rebase true && \ - git config --global init.defaultBranch master && \ - echo "> configure bash" && \ - echo "alias ls='ls --color=auto'" >> /etc/bash.bashrc && \ - echo "alias buni='bun i --no-save'" >> /etc/bash.bashrc && \ - echo "alias bat='batcat'" >> /etc/bash.bashrc && \ - echo ". /usr/local/share/nvm/nvm.sh" >> /etc/bash.bashrc && \ - echo ". /usr/share/bash-completion/completions/git" >> /etc/bash.bashrc && \ - echo 'PS1="\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ "' >> /etc/bash.bashrc && \ - echo "> setup inputrc" && \ - echo "set editing-mode vi" >> /etc/inputrc && \ - echo "TAB: menu-complete" >> /etc/inputrc && \ - echo "set UTF-8" && \ - echo " > configure languages" && \ - echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \ - echo "ru_RU.UTF-8 UTF-8" >> /etc/locale.gen && \ - echo "uk_UA.UTF-8 UTF-8" >> /etc/locale.gen && \ - echo "es_ES.UTF-8 UTF-8" >> /etc/locale.gen && \ - echo "ja_JP.UTF-8 UTF-8" >> /etc/locale.gen && \ - echo "el_GR.UTF-8 UTF-8" >> /etc/locale.gen && \ - locale-gen - -COPY . /usr/local/share/cloudcmd - -WORKDIR / - -ENV cloudcmd_terminal=true \ - cloudcmd_terminal_path=gritty \ - cloudcmd_vim=true \ - cloudcmd_open=false \ - PATH=node_modules/.bin:$PATH \ - PATH=~/.local/bin:$PATH \ - BUN_INSTALL_CACHE_DIR=/tmp/bun-cache \ - DENO_DIR=/tmp/deno-cache \ - LANG=en_US.UTF-8 \ - LANGUAGE=en_US:en \ - LC_ALL=en_US.UTF-8 \ - TERM=xterm-256color \ - XDG_CACHE_HOME=/tmp \ - XDG_DATA_HOME=/usr/local/share \ - XDG_CONFIG_HOME=~/.config - -EXPOSE 8000 - -ENTRYPOINT ["/usr/local/share/cloudcmd/bin/cloudcmd.js"] diff --git a/docker/Dockerfile.slim b/docker/Dockerfile.slim deleted file mode 100644 index c6f273b8..00000000 --- a/docker/Dockerfile.slim +++ /dev/null @@ -1,39 +0,0 @@ -FROM node:slim AS build - -RUN mkdir -p /usr/src/cloudcmd/ - -WORKDIR /usr/src/cloudcmd - -COPY package.json /usr/src/cloudcmd/ - -RUN apt-get update && \ - apt-get install -y build-essential python3 libncurses5-dev pkg-config && \ - apt-get install -y --no-install-recommends curl ca-certificates unzip && \ - curl -fsSL https://bun.sh/install | bash && \ - ln -s ~/.bun/bin/bun /usr/local/bin/bun && \ - chmod +x /usr/local/bin/bun && \ - bun r gritty --omit dev && \ - bun i gritty --omit dev && \ - ~/.bun/bin/bun pm cache rm && \ - rm -rf /var/lib/apt/lists/* - -COPY . /usr/src/cloudcmd - -FROM node:slim AS runtime - -COPY --from=build /usr/src/cloudcmd /usr/src/cloudcmd -COPY --from=build /root/.bun /root/.bun - -LABEL maintainer="Coderaiser" -LABEL org.opencontainers.image.source="https://github.com/coderaiser/cloudcmd" - -WORKDIR / - -ENV cloudcmd_terminal=true -ENV cloudcmd_terminal_path=gritty -ENV cloudcmd_open=false -ENV PATH="/root/.bun/bin:$PATH" - -EXPOSE 8000 - -ENTRYPOINT ["/usr/src/cloudcmd/bin/cloudcmd.js"] diff --git a/eslint.config.js b/eslint.config.mjs similarity index 68% rename from eslint.config.js rename to eslint.config.mjs index 22add654..cb783900 100644 --- a/eslint.config.js +++ b/eslint.config.mjs @@ -1,25 +1,34 @@ import {safeAlign} from 'eslint-plugin-putout'; import {defineConfig} from 'eslint/config'; +import n from 'eslint-plugin-n'; import globals from 'globals'; import {matchToFlat} from '@putout/eslint-flat'; export const match = { - 'bin/release.js': { + 'bin/release.mjs': { 'no-console': 'off', 'n/hashbang': 'off', }, - 'client/dom/index.*': { + 'client/dom/index.js': { 'no-multi-spaces': 'off', }, - 'client/**': { + '{client,static}/**/*.{js,mjs}': { + 'n/no-extraneous-require': 'off', 'n/no-unsupported-features/node-builtins': 'off', }, + 'bin/cloudcmd.js': { + 'no-console': 'off', + }, }; export default defineConfig([ safeAlign, { ignores: ['**/fixture'], rules: { 'key-spacing': 'off', + 'n/prefer-node-protocol': 'error', + }, + plugins: { + n, }, }, { files: ['{client,common,static}/**/*.js'], diff --git a/html/index.html b/html/index.html index d366ed1c..0cfc73c5 100644 --- a/html/index.html +++ b/html/index.html @@ -9,6 +9,7 @@ + @@ -44,7 +45,7 @@