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 73% rename from .madrun.js rename to .madrun.mjs index efa8ce98..733499f3 100644 --- a/.madrun.js +++ b/.madrun.mjs @@ -1,45 +1,49 @@ +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': () => 'redlint scan; 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`, '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: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', 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/.rspack/css.js b/.rspack/css.js deleted file mode 100644 index 10d28250..00000000 --- a/.rspack/css.js +++ /dev/null @@ -1,38 +0,0 @@ -import {env} from 'node:process'; -import {rspack} from '@rspack/core'; - -const {CssExtractRspackPlugin} = rspack; -const isDev = env.NODE_ENV === 'development'; - -const plugins = [ - new CssExtractRspackPlugin({ - filename: '[name].css', - }), -]; - -const rules = [{ - test: /\.css$/i, - use: [CssExtractRspackPlugin.loader, { - loader: 'css-loader', - options: { - url: true, - }, - }], -}, { - test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/, - type: 'asset/inline', -}]; - -export default { - mode: isDev ? 'development' : 'production', - plugins, - module: { - rules, - }, - optimization: { - minimize: !isDev, - minimizer: [ - new rspack.LightningCssMinimizerRspackPlugin(), - ], - }, -}; diff --git a/.webpack/css.mjs b/.webpack/css.mjs new file mode 100644 index 00000000..3b83715b --- /dev/null +++ b/.webpack/css.mjs @@ -0,0 +1,34 @@ +import {env} from 'node:process'; +import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; + +const isDev = env.NODE_ENV === 'development'; + +const clean = (a) => a.filter(Boolean); + +const plugins = clean([ + new MiniCssExtractPlugin({ + filename: '[name].css', + }), + !isDev && new OptimizeCssAssetsPlugin(), +]); + +const rules = [{ + test: /\.css$/i, + use: [MiniCssExtractPlugin.loader, { + loader: 'css-loader', + options: { + url: true, + }, + }], +}, { + test: /\.(png|gif|woff|woff2|eot|ttf)$/, + type: 'asset/inline', +}]; + +export default { + plugins, + module: { + rules, + }, +}; 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 72% rename from .rspack/js.js rename to .webpack/js.mjs index 251d85f6..f2901384 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,7 +52,8 @@ const plugins = [ new EnvironmentPlugin({ NODE_ENV, }), - new ProvidePlugin({ + new WebpackBar(), + new webpack.ProvidePlugin({ process: 'process/browser', }), ]; @@ -83,8 +77,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 +97,9 @@ 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'), + util: import.meta.resolve('util'), }, }, devtool, @@ -119,33 +111,32 @@ 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`, [`${modules}/edit-names`]: `${dirModules}/edit-names.js`, [`${modules}/edit-names-vim`]: `${dirModules}/edit-names-vim.js`, - [`${modules}/menu`]: `${dirModules}/menu/index.js`, + [`${modules}/menu`]: `${dirModules}/menu/index.mjs`, [`${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}/upload`]: `${dirModules}/upload.mjs`, + [`${modules}/operation`]: `${dirModules}/operation/index.mjs`, [`${modules}/konsole`]: `${dirModules}/konsole.js`, [`${modules}/terminal`]: `${dirModules}/terminal.js`, [`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`, - [`${modules}/cloud`]: `${dirModules}/cloud.js`, - [`${modules}/user-menu`]: `${dirModules}/user-menu/index.js`, + [`${modules}/cloud`]: `${dirModules}/cloud.mjs`, + [`${modules}/user-menu`]: `${dirModules}/user-menu/index.mjs`, [`${modules}/polyfill`]: `${dirModules}/polyfill.js`, [`${modules}/command-line`]: `${dirModules}/command-line.js`, }, @@ -162,7 +153,7 @@ export default { }, plugins, performance: { - maxEntrypointSize: 800_000, + maxEntrypointSize: 600_000, maxAssetSize: 600_000, }, }; diff --git a/ChangeLog b/ChangeLog index c5e0fbca..e87bb209 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,641 +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: diff --git a/HELP.md b/HELP.md index 2967f0d0..0831dcb6 100644 --- a/HELP.md +++ b/HELP.md @@ -1,6 +1,6 @@ -# Cloud Commander v19.19.1 +# Cloud Commander v19.1.18 -### [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,111 +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)** diff --git a/README.md b/README.md index dfe8d8c6..5d91496b 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.18 [![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 81% rename from client/cloudcmd.js rename to client/cloudcmd.mjs index f2416fd4..5a207f8a 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 * as CloudFunc from '../common/cloudfunc.mjs'; +import DOM from './dom/index.mjs'; +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 100% rename from client/dom/buffer.js rename to client/dom/buffer.mjs diff --git a/client/dom/cmd.js b/client/dom/cmd.mjs similarity index 100% rename from client/dom/cmd.js rename to client/dom/cmd.mjs 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..124c1e62 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.mjs similarity index 92% rename from client/dom/dialog.js rename to client/dom/dialog.mjs index b1791d92..0a1e449c 100644 --- a/client/dom/dialog.js +++ b/client/dom/dialog.mjs @@ -1,5 +1,5 @@ import {tryToCatch} from 'try-to-catch'; -import * as smalltalk from 'smalltalk'; +import smalltalk from 'smalltalk'; const title = 'Cloud Commander'; diff --git a/client/dom/directory.js b/client/dom/directory.mjs similarity index 95% rename from client/dom/directory.js rename to client/dom/directory.mjs index efbca119..e0cabab7 100644 --- a/client/dom/directory.js +++ b/client/dom/directory.mjs @@ -1,8 +1,8 @@ /* global DOM, CloudCmd */ import philip from 'philip'; import * as Dialog from '#dom/dialog'; -import {FS} from '#common/cloudfunc'; -import * as Images from '#dom/images'; +import * as Images from './images.mjs'; +import {FS} from '../../common/cloudfunc.mjs'; export const uploadDirectory = (items) => { if (items.length) diff --git a/client/dom/dom-tree.js b/client/dom/dom-tree.mjs similarity index 100% rename from client/dom/dom-tree.js rename to client/dom/dom-tree.mjs diff --git a/client/dom/dom-tree.spec.js b/client/dom/dom-tree.spec.js deleted file mode 100644 index 53b376c7..00000000 --- a/client/dom/dom-tree.spec.js +++ /dev/null @@ -1,152 +0,0 @@ -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'; - -test('dom: isContainClass: no element', (t) => { - const [e] = tryCatch(isContainClass); - - t.equal(e.message, 'element could not be empty!', 'should throw when no element'); - t.end(); -}); - -test('dom: isContainClass: no className', (t) => { - const [e] = tryCatch(isContainClass, {}); - - t.equal(e.message, 'className could not be empty!', 'should throw when no element'); - t.end(); -}); - -test('dom: isContainClass: contains', (t) => { - const el = create(); - const {contains} = el.classList; - - const className = 'hello'; - isContainClass(el, className); - - t.calledWith(contains, [className], 'should call contains'); - t.end(); -}); - -test('dom: isContainClass: contains: array', (t) => { - const el = create(); - const {contains} = el.classList; - - const className = 'hello'; - isContainClass(el, ['world', className, 'hello']); - - 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/dom-tree.spec.mjs b/client/dom/dom-tree.spec.mjs new file mode 100644 index 00000000..a711dcb6 --- /dev/null +++ b/client/dom/dom-tree.spec.mjs @@ -0,0 +1,40 @@ +import test from 'supertape'; +import {create} from 'auto-globals'; +import {tryCatch} from 'try-catch'; +import {isContainClass} from './dom-tree.mjs'; + +test('dom: isContainClass: no element', (t) => { + const [e] = tryCatch(isContainClass); + + t.equal(e.message, 'element could not be empty!', 'should throw when no element'); + t.end(); +}); + +test('dom: isContainClass: no className', (t) => { + const [e] = tryCatch(isContainClass, {}); + + t.equal(e.message, 'className could not be empty!', 'should throw when no element'); + t.end(); +}); + +test('dom: isContainClass: contains', (t) => { + const el = create(); + const {contains} = el.classList; + + const className = 'hello'; + isContainClass(el, className); + + t.calledWith(contains, [className], 'should call contains'); + t.end(); +}); + +test('dom: isContainClass: contains: array', (t) => { + const el = create(); + const {contains} = el.classList; + + const className = 'hello'; + isContainClass(el, ['world', className, 'hello']); + + t.calledWith(contains, [className], 'should call contains'); + 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.mjs similarity index 100% rename from client/dom/files.js rename to client/dom/files.mjs 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.mjs similarity index 96% rename from client/dom/index.js rename to client/dom/index.mjs index a82b6f72..471f3b7c 100644 --- a/client/dom/index.js +++ b/client/dom/index.mjs @@ -6,16 +6,16 @@ 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 * as Images from './images.mjs'; 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'; +import * as CurrentFile from './current-file.mjs'; +import * as DOMTree from './dom-tree.mjs'; +import * as Cmd from './cmd.mjs'; +import IO from './io/index.js'; +import {uploadDirectory} from './directory.mjs'; +import * as Buffer from './buffer.mjs'; +import {loadRemote as _loadRemote} from './load-remote.mjs'; +import {selectByPattern} from './select-by-pattern.mjs'; const {assign} = Object; @@ -442,7 +442,11 @@ export function getFilenames(files) { if (name === '..') allFiles.shift(); - return allFiles.map(DOM.getCurrentName); + const names = allFiles.map((current) => { + return DOM.getCurrentName(current); + }); + + return names; } /** @@ -701,10 +705,7 @@ export function getPackerExt(type) { export async function goToDirectory(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; diff --git a/client/dom/index.spec.js b/client/dom/index.spec.mjs similarity index 95% rename from client/dom/index.spec.js rename to client/dom/index.spec.mjs index 268efe5f..fb4de9e4 100644 --- a/client/dom/index.spec.js +++ b/client/dom/index.spec.mjs @@ -1,5 +1,5 @@ import {test, stub} from 'supertape'; -import {getCSSVar, goToDirectory} from '#dom'; +import {getCSSVar, goToDirectory} from './index.mjs'; 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..8f9197b5 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('#dom/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.mjs similarity index 78% rename from client/dom/load-remote.js rename to client/dom/load-remote.mjs index 5be8e327..974763e0 100644 --- a/client/dom/load-remote.js +++ b/client/dom/load-remote.mjs @@ -1,8 +1,8 @@ /* global CloudCmd */ import {callbackify} from 'node:util'; -import {rendy} from 'rendy'; +import rendy from 'rendy'; import itype from 'itype'; -import * as load from 'load.js'; +import load from 'load.js'; import {tryToCatch} from 'try-to-catch'; import {findObjByNameInArr} from '#common/util'; import * as Files from '#dom/files'; @@ -33,19 +33,13 @@ export const loadRemote = callbackify(async (name, options) => { local = [module.local]; } - const localURL = []; + const localURL = local.map((url) => prefix + url); - for (const url of local) { - localURL.push(prefix + url); - } - - const remoteURL = []; - - for (const tmpl of remoteTmpls) { - remoteURL.push(rendy(tmpl, { + const remoteURL = remoteTmpls.map((tmpl) => { + return rendy(tmpl, { version, - })); - } + }); + }); if (online) { const [e] = await tryToCatch(load.parallel, remoteURL); diff --git a/client/dom/load.js b/client/dom/load.mjs similarity index 98% rename from client/dom/load.js rename to client/dom/load.mjs index 8c7011c1..02158a83 100644 --- a/client/dom/load.js +++ b/client/dom/load.mjs @@ -2,7 +2,7 @@ import itype from 'itype'; import jonny from 'jonny'; import Emitify from 'emitify'; import exec from 'execon'; -import * as Images from '#dom/images'; +import * as Images from './images.mjs'; /** * Function gets id by src diff --git a/client/dom/operations/rename-current.js b/client/dom/operations/rename-current.js index 8b6536d3..e8d29987 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('#dom/dialog'); +const Storage = require('#dom/storage'); +const RESTful = require('#dom/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.mjs similarity index 85% rename from client/dom/rest.js rename to client/dom/rest.mjs index 3cf2fb66..092096df 100644 --- a/client/dom/rest.js +++ b/client/dom/rest.mjs @@ -1,8 +1,8 @@ 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'; +import {encode} from '../../common/entity.js'; +import * as Images from './images.mjs'; +import IO from './io/index.js'; const handleError = (promise) => async (...args) => { const [e, data] = await tryToCatch(promise, ...args); @@ -18,7 +18,7 @@ const handleError = (promise) => async (...args) => { return [e, data]; }; -export const remove = handleError(IO.remove); +export const remove = handleError(IO.delete); export const patch = handleError(IO.patch); export const write = handleError(IO.write); export const createDirectory = handleError(IO.createDirectory); diff --git a/client/dom/select-by-pattern.js b/client/dom/select-by-pattern.mjs similarity index 93% rename from client/dom/select-by-pattern.js rename to client/dom/select-by-pattern.mjs index 0bada6ef..be992878 100644 --- a/client/dom/select-by-pattern.js +++ b/client/dom/select-by-pattern.mjs @@ -1,10 +1,10 @@ import {alert, prompt} from '#dom/dialog'; import {getRegExp} from '#common/util'; -import {getCurrentName} from './current-file.js'; +import {getCurrentName} from './current-file.mjs'; import { isSelected, toggleSelectedFile, -} from './cmd.js'; +} from './cmd.mjs'; let SelectType = '*.*'; diff --git a/client/dom/storage.js b/client/dom/storage.mjs similarity index 100% rename from client/dom/storage.js rename to client/dom/storage.mjs diff --git a/client/dom/storage.spec.js b/client/dom/storage.spec.mjs similarity index 100% rename from client/dom/storage.spec.js rename to client/dom/storage.spec.mjs diff --git a/client/dom/upload-files.js b/client/dom/upload-files.mjs similarity index 90% rename from client/dom/upload-files.js rename to client/dom/upload-files.mjs index 1c37fe1b..908c402b 100644 --- a/client/dom/upload-files.js +++ b/client/dom/upload-files.mjs @@ -3,9 +3,9 @@ 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'; +import * as Images from './images.mjs'; +import {FS} from '../../common/cloudfunc.mjs'; +import {getCurrentDirPath} from './current-file.mjs'; const loadFile = wraptile(_loadFile); const onEnd = wraptile(_onEnd); 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.mjs similarity index 100% rename from client/key/binder.js rename to client/key/binder.mjs 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 96% rename from client/key/index.js rename to client/key/index.mjs index 283a7f11..06de23c8 100644 --- a/client/key/index.js +++ b/client/key/index.mjs @@ -2,19 +2,19 @@ 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 _vim from './vim/index.js'; -import setCurrentByChar from './set-current-by-char.js'; -import {createBinder} from './binder.js'; +import * as Buffer from '../dom/buffer.mjs'; +import * as KEY from './key.mjs'; +import _vim from './vim/index.mjs'; +import setCurrentByChar from './set-current-by-char.mjs'; +import {createBinder} from './binder.mjs'; 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'; @@ -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.mjs similarity index 88% rename from client/key/index.spec.js rename to client/key/index.spec.mjs index bec92b31..ad4753fb 100644 --- a/client/key/index.spec.js +++ b/client/key/index.spec.mjs @@ -1,8 +1,8 @@ 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'; +import {ESC} from './key.mjs'; +import {Key, _listener} from './index.mjs'; +import {getDOM, getCloudCmd} from './vim/globals.fixture.mjs'; const test = autoGlobals(supertape); const {stub} = supertape; @@ -37,7 +37,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.mjs similarity index 100% rename from client/key/set-current-by-char.js rename to client/key/set-current-by-char.mjs diff --git a/client/key/vim/find.js b/client/key/vim/find.mjs similarity index 100% rename from client/key/vim/find.js rename to client/key/vim/find.mjs diff --git a/client/key/vim/find.spec.js b/client/key/vim/find.spec.js deleted file mode 100644 index 191a3652..00000000 --- a/client/key/vim/find.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -import test from 'supertape'; -import {getDOM} from './globals.fixture.js'; -import { - _next, - _previous, - find, - findNext, - findPrevious, -} from './find.js'; - -globalThis.DOM = getDOM(); - -test('cloudcmd: client: vim: _next', (t) => { - const result = _next(1, 2); - - t.notOk(result, 'should return 0'); - 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/find.spec.mjs b/client/key/vim/find.spec.mjs new file mode 100644 index 00000000..ee9a9458 --- /dev/null +++ b/client/key/vim/find.spec.mjs @@ -0,0 +1,19 @@ +import test from 'supertape'; +import {getDOM} from './globals.fixture.mjs'; +import {_next, _previous} from './find.mjs'; + +globalThis.DOM = getDOM(); + +test('cloudcmd: client: vim: _next', (t) => { + const result = _next(1, 2); + + t.notOk(result, 'should return 0'); + t.end(); +}); + +test('cloudcmd: client: vim: _previous', (t) => { + const result = _previous(0, 2); + + t.equal(result, 1, 'should return 1'); + t.end(); +}); diff --git a/client/key/vim/globals.fixture.js b/client/key/vim/globals.fixture.mjs similarity index 100% rename from client/key/vim/globals.fixture.js rename to client/key/vim/globals.fixture.mjs diff --git a/client/key/vim/index.js b/client/key/vim/index.mjs similarity index 87% rename from client/key/vim/index.js rename to client/key/vim/index.mjs index 981f6f19..2bff53ee 100644 --- a/client/key/vim/index.js +++ b/client/key/vim/index.mjs @@ -1,9 +1,11 @@ -import vim from './vim.js'; -import * as finder from './find.js'; +/* global CloudCmd */ +/* global DOM */ +import vim from './vim.mjs'; +import * as finder from './find.mjs'; import { setCurrent, selectFileNotParent, -} from './set-current.js'; +} from './set-current.mjs'; export default (key, event, overrides = {}) => { const defaults = { @@ -33,13 +35,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 +57,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: () => { diff --git a/client/key/vim/index.spec.js b/client/key/vim/index.spec.mjs similarity index 86% rename from client/key/vim/index.spec.js rename to client/key/vim/index.spec.mjs index 231c6577..10392c32 100644 --- a/client/key/vim/index.spec.js +++ b/client/key/vim/index.spec.mjs @@ -1,7 +1,6 @@ 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'; +import {getDOM, getCloudCmd} from './globals.fixture.mjs'; +import vim, {selectFile as vimSelectFile} from './index.mjs'; globalThis.DOM = getDOM(); globalThis.CloudCmd = getCloudCmd(); @@ -448,7 +447,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 +463,6 @@ test('cloudcmd: client: key: visual', (t) => { test('cloudcmd: client: key: ESC', (t) => { const element = {}; const unselectFiles = stub(); - const Info = { element, }; @@ -511,7 +508,6 @@ test('cloudcmd: client: key: Enter', async (t) => { test('cloudcmd: client: key: /', (t) => { const preventDefault = stub(); const element = {}; - const Info = { element, files: [], @@ -658,99 +654,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.mjs similarity index 100% rename from client/key/vim/set-current.js rename to client/key/vim/set-current.mjs diff --git a/client/key/vim/vim.js b/client/key/vim/vim.mjs similarity index 91% rename from client/key/vim/vim.js rename to client/key/vim/vim.mjs index 95a27caf..0d0404c3 100644 --- a/client/key/vim/vim.js +++ b/client/key/vim/vim.mjs @@ -40,9 +40,6 @@ export default (key, operations = {}) => { makeDirectory = noop, terminal = noop, edit = noop, - operationCopy = noop, - operationMove = noop, - rename = noop, } = operations; if (key === 'Enter') @@ -114,26 +111,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.mjs similarity index 78% rename from client/key/vim/vim.spec.js rename to client/key/vim/vim.spec.mjs index 02fd52d4..180f114f 100644 --- a/client/key/vim/vim.spec.js +++ b/client/key/vim/vim.spec.mjs @@ -1,5 +1,5 @@ import {test, stub} from 'supertape'; -import vim from './vim.js'; +import vim from './vim.mjs'; test('vim: no operations', (t) => { const result = vim('hello', {}); @@ -43,28 +43,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..99514b98 100644 --- a/client/listeners/index.js +++ b/client/listeners/index.mjs @@ -6,14 +6,14 @@ 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 {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.mjs similarity index 96% rename from client/modules/cloud.js rename to client/modules/cloud.mjs index cfe0d708..f0fbb31e 100644 --- a/client/modules/cloud.js +++ b/client/modules/cloud.mjs @@ -4,7 +4,7 @@ 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'; +import * as Images from '../dom/images.mjs'; const {log} = CloudCmd; diff --git a/client/modules/command-line.js b/client/modules/command-line.js index 7a1c3993..89a562fa 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 93% rename from client/modules/config/index.js rename to client/modules/config/index.mjs index dfa4c105..ad7b2acb 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'; @@ -9,11 +9,10 @@ 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 {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..7aa6715c 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..3ccd5805 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.mjs similarity index 98% rename from client/modules/menu/index.js rename to client/modules/menu/index.mjs index 7235591e..a8c7d0df 100644 --- a/client/modules/menu/index.js +++ b/client/modules/menu/index.mjs @@ -1,11 +1,11 @@ +/* global CloudCmd, DOM */ 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'; +import {FS} from '../../../common/cloudfunc.mjs'; -const {CloudCmd, DOM} = globalThis; const {config, Key} = CloudCmd; const { @@ -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; diff --git a/client/modules/operation/format.js b/client/modules/operation/format.mjs similarity index 100% rename from client/modules/operation/format.js rename to client/modules/operation/format.mjs diff --git a/client/modules/operation/get-next-current-name.js b/client/modules/operation/get-next-current-name.mjs similarity index 100% rename from client/modules/operation/get-next-current-name.js rename to client/modules/operation/get-next-current-name.mjs 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..a2e7c0e7 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.mjs'; +import {setListeners} from './set-listeners.mjs'; +import {getNextCurrentName} from './get-next-current-name.mjs'; 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.mjs similarity index 100% rename from client/modules/operation/remove-extension.js rename to client/modules/operation/remove-extension.mjs diff --git a/client/modules/operation/remove-extension.spec.js b/client/modules/operation/remove-extension.spec.mjs similarity index 92% rename from client/modules/operation/remove-extension.spec.js rename to client/modules/operation/remove-extension.spec.mjs index fa11e9e2..8dbacb61 100644 --- a/client/modules/operation/remove-extension.spec.js +++ b/client/modules/operation/remove-extension.spec.mjs @@ -1,5 +1,5 @@ import test from 'supertape'; -import {removeExtension} from './remove-extension.js'; +import {removeExtension} from './remove-extension.mjs'; 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 85% rename from client/modules/operation/set-listeners.js rename to client/modules/operation/set-listeners.mjs index e54e7d14..355aee79 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.mjs'; 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..8b24fd2a 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/index.mjs'); +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..b5850526 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/index.mjs'); +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.mjs similarity index 96% rename from client/modules/upload.js rename to client/modules/upload.mjs index 8433fbf5..152f8b97 100644 --- a/client/modules/upload.js +++ b/client/modules/upload.mjs @@ -2,7 +2,7 @@ import createElement from '@cloudcmd/create-element'; import * as Files from '#dom/files'; import {uploadFiles} from '#dom/upload-files'; -import * as Images from '#dom/images'; +import * as Images from '../dom/images.mjs'; CloudCmd.Upload = { init, diff --git a/client/modules/user-menu/get-user-menu.js b/client/modules/user-menu/get-user-menu.mjs similarity index 100% rename from client/modules/user-menu/get-user-menu.js rename to client/modules/user-menu/get-user-menu.mjs diff --git a/client/modules/user-menu/get-user-menu.spec.js b/client/modules/user-menu/get-user-menu.spec.mjs similarity index 89% rename from client/modules/user-menu/get-user-menu.spec.js rename to client/modules/user-menu/get-user-menu.spec.mjs index 606eb820..aae42909 100644 --- a/client/modules/user-menu/get-user-menu.spec.js +++ b/client/modules/user-menu/get-user-menu.spec.mjs @@ -1,5 +1,5 @@ import test from 'supertape'; -import {getUserMenu} from './get-user-menu.js'; +import {getUserMenu} from './get-user-menu.mjs'; test('user-menu: getUserMenu', (t) => { const menu = `module.exports = { diff --git a/client/modules/user-menu/index.js b/client/modules/user-menu/index.mjs similarity index 91% rename from client/modules/user-menu/index.js rename to client/modules/user-menu/index.mjs index 60d32d95..9b85612c 100644 --- a/client/modules/user-menu/index.js +++ b/client/modules/user-menu/index.mjs @@ -1,4 +1,4 @@ -import '../../../css/user-menu.css'; +/* global CloudCmd, DOM */ import currify from 'currify'; import wraptile from 'wraptile'; import {fullstore} from 'fullstore'; @@ -8,22 +8,17 @@ 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'; +import '../../../css/user-menu.css'; +import * as Images from '../../dom/images.mjs'; +import {getUserMenu} from './get-user-menu.mjs'; +import {navigate} from './navigate.mjs'; +import {parseError} from './parse-error.mjs'; +import {parseUserMenu} from './parse-user-menu.mjs'; +import {runSelected} from './run.mjs'; const loadCSS = load.css; const sourceStore = fullstore(); -const { - CloudCmd, - DOM, - CloudFunc, -} = globalThis; - const Name = 'UserMenu'; CloudCmd[Name] = { @@ -151,7 +146,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.mjs similarity index 97% rename from client/modules/user-menu/navigate.js rename to client/modules/user-menu/navigate.mjs index 0fa4d672..2ebebd17 100644 --- a/client/modules/user-menu/navigate.js +++ b/client/modules/user-menu/navigate.mjs @@ -4,7 +4,7 @@ import { K, UP, DOWN, -} from '../../key/key.js'; +} from '../../key/key.mjs'; const store = fullstore(1); const isDigit = (a) => /^\d+$/.test(a); diff --git a/client/modules/user-menu/navigate.spec.js b/client/modules/user-menu/navigate.spec.mjs similarity index 97% rename from client/modules/user-menu/navigate.spec.js rename to client/modules/user-menu/navigate.spec.mjs index 6539c8e9..a2de328b 100644 --- a/client/modules/user-menu/navigate.spec.js +++ b/client/modules/user-menu/navigate.spec.mjs @@ -1,11 +1,11 @@ import test from 'supertape'; -import {navigate} from './navigate.js'; +import {navigate} from './navigate.mjs'; import { UP, DOWN, J, K, -} from '../../key/key.js'; +} from '../../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.mjs similarity index 100% rename from client/modules/user-menu/parse-error.js rename to client/modules/user-menu/parse-error.mjs diff --git a/client/modules/user-menu/parse-error.spec.js b/client/modules/user-menu/parse-error.spec.mjs similarity index 94% rename from client/modules/user-menu/parse-error.spec.js rename to client/modules/user-menu/parse-error.spec.mjs index 5ec8da45..cef3ae55 100644 --- a/client/modules/user-menu/parse-error.spec.js +++ b/client/modules/user-menu/parse-error.spec.mjs @@ -1,5 +1,5 @@ import test from 'supertape'; -import {parseError} from './parse-error.js'; +import {parseError} from './parse-error.mjs'; 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.mjs similarity index 100% rename from client/modules/user-menu/parse-user-menu.js rename to client/modules/user-menu/parse-user-menu.mjs diff --git a/client/modules/user-menu/parse-user-menu.spec.js b/client/modules/user-menu/parse-user-menu.spec.mjs similarity index 91% rename from client/modules/user-menu/parse-user-menu.spec.js rename to client/modules/user-menu/parse-user-menu.spec.mjs index fb763209..a7914152 100644 --- a/client/modules/user-menu/parse-user-menu.spec.js +++ b/client/modules/user-menu/parse-user-menu.spec.mjs @@ -1,10 +1,9 @@ import {test, stub} from 'supertape'; -import {parseUserMenu} from './parse-user-menu.js'; +import {parseUserMenu} from './parse-user-menu.mjs'; test('cloudcmd: user menu: parse', (t) => { const fn = stub(); const __settings = {}; - const result = parseUserMenu({ __settings, 'F2 - Rename file': fn, diff --git a/client/modules/user-menu/run.js b/client/modules/user-menu/run.mjs similarity index 100% rename from client/modules/user-menu/run.js rename to client/modules/user-menu/run.mjs diff --git a/client/modules/user-menu/run.spec.js b/client/modules/user-menu/run.spec.mjs similarity index 89% rename from client/modules/user-menu/run.spec.js rename to client/modules/user-menu/run.spec.mjs index 86738712..8cac782d 100644 --- a/client/modules/user-menu/run.spec.js +++ b/client/modules/user-menu/run.spec.mjs @@ -1,5 +1,5 @@ import {test, stub} from 'supertape'; -import {runSelected} from './run.js'; +import {runSelected} from './run.mjs'; 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..47724798 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 95% rename from client/sort.js rename to client/sort.mjs index ed0971f3..8ac12452 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.mjs'; 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 100% rename from client/sw/sw.js rename to client/sw/sw.mjs 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.mjs similarity index 98% rename from common/util.js rename to common/util.mjs index 422b3c22..761b6345 100644 --- a/common/util.js +++ b/common/util.mjs @@ -45,7 +45,7 @@ export const getExt = (name) => { }; /** - * find object by name in array + * find object by name in arrray * * @param array * @param name diff --git a/common/util.spec.js b/common/util.spec.mjs similarity index 82% rename from common/util.spec.js rename to common/util.spec.mjs index 5a19a8be..870e55e8 100644 --- a/common/util.spec.js +++ b/common/util.spec.mjs @@ -5,8 +5,6 @@ import { getRegExp, escapeRegExp, getExt, - time, - timeEnd, } from '#common/util'; test('getExt: no extension', (t) => { @@ -65,7 +63,6 @@ test('util: findObjByNameInArr: object', (t) => { test('util: findObjByNameInArr: array', (t) => { const name = 'hello'; const data = 'abc'; - const item = { name, data, @@ -108,31 +105,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 70% rename from eslint.config.js rename to eslint.config.mjs index 22add654..dc751dca 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.*': { '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 @@