From 12b2432c2113fcec75b4f8e6a123e9693da66434 Mon Sep 17 00:00:00 2001 From: butlerx Date: Mon, 31 Aug 2020 21:22:26 +0100 Subject: [PATCH] place all compiled assets in build folder --- .eslintignore | 2 +- .eslintrc.json | 20 ++- containers/wetty/Dockerfile | 4 +- package.json | 22 ++- {assets => src/assets}/favicon.ico | Bin {assets => src/assets}/scss/options.scss | 0 {assets => src/assets}/scss/overlay.scss | 0 {assets => src/assets}/scss/styles.scss | 0 {assets => src/assets}/scss/terminal.scss | 0 {assets => src/assets}/scss/variables.scss | 0 src/buffer.ts | 6 +- src/client/client/options.ts | 12 -- src/client/dev.ts | 5 + src/client/index.ts | 161 ------------------ src/client/shared/elements.ts | 3 + src/client/wetty.ts | 74 ++++++++ .../copyToClipboard.ts => wetty/clipboard.ts} | 13 +- src/client/{client => wetty}/disconnect.ts | 6 +- src/client/{client => wetty}/download.spec.ts | 0 src/client/{client => wetty}/download.ts | 75 ++++++-- src/client/{client => wetty}/mobile.ts | 4 +- src/client/wetty/options.ts | 51 ++++++ src/client/{client => wetty}/socket.ts | 2 +- src/server.ts | 1 + src/server/socketServer.ts | 27 +-- src/server/socketServer/html.ts | 24 ++- src/shared/env.ts | 1 + src/shared/logger.ts | 6 +- tsconfig.json | 2 +- 29 files changed, 271 insertions(+), 250 deletions(-) rename {assets => src/assets}/favicon.ico (100%) rename {assets => src/assets}/scss/options.scss (100%) rename {assets => src/assets}/scss/overlay.scss (100%) rename {assets => src/assets}/scss/styles.scss (100%) rename {assets => src/assets}/scss/terminal.scss (100%) rename {assets => src/assets}/scss/variables.scss (100%) delete mode 100644 src/client/client/options.ts create mode 100644 src/client/dev.ts delete mode 100644 src/client/index.ts create mode 100644 src/client/wetty.ts rename src/client/{client/copyToClipboard.ts => wetty/clipboard.ts} (66%) rename src/client/{client => wetty}/disconnect.ts (64%) rename src/client/{client => wetty}/download.spec.ts (100%) rename src/client/{client => wetty}/download.ts (62%) rename src/client/{client => wetty}/mobile.ts (83%) create mode 100644 src/client/wetty/options.ts rename src/client/{client => wetty}/socket.ts (83%) create mode 100644 src/shared/env.ts diff --git a/.eslintignore b/.eslintignore index e6bd6c7..b8c8eed 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,4 +3,4 @@ node_modules/ lib public/ *hterm* -web_modules +build diff --git a/.eslintrc.json b/.eslintrc.json index b237129..3799e81 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,9 @@ { "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "prettier"], + "plugins": [ + "@typescript-eslint", + "prettier" + ], "env": { "es6": true, "node": true, @@ -14,8 +17,14 @@ "prettier/@typescript-eslint" ], "rules": { - "linebreak-style": ["error", "unix"], - "arrow-parens": ["error", "as-needed"], + "linebreak-style": [ + "error", + "unix" + ], + "arrow-parens": [ + "error", + "as-needed" + ], "no-param-reassign": [ "error", { @@ -61,7 +70,10 @@ "settings": { "import/resolver": { "node": { - "extensions": [".ts", ".js"] + "extensions": [ + ".ts", + ".js" + ] } } } diff --git a/containers/wetty/Dockerfile b/containers/wetty/Dockerfile index 12db737..22d31dc 100644 --- a/containers/wetty/Dockerfile +++ b/containers/wetty/Dockerfile @@ -1,4 +1,4 @@ -FROM node:dubnium-alpine as builder +FROM node:current-alpine as builder RUN apk add -U build-base python WORKDIR /usr/src/app COPY . /usr/src/app @@ -6,7 +6,7 @@ RUN yarn && \ yarn build && \ yarn install --production --ignore-scripts --prefer-offline -FROM node:dubnium-alpine +FROM node:current-alpine LABEL maintainer="butlerx@notthe.cloud" WORKDIR /usr/src/app ENV NODE_ENV=production diff --git a/package.json b/package.json index bad9809..24520d0 100644 --- a/package.json +++ b/package.json @@ -5,22 +5,20 @@ "homepage": "https://github.com/butlerx/wetty", "license": "MIT", "type": "module", - "main": "./lib/main.js", - "module": "./lib/server.js", + "main": "./build/main.js", + "module": "./build/server.js", "files": [ - "lib/", - "assets/css" + "build/" ], "scripts": { - "build": "snowpack build && tsc -p tsconfig.json", - "contributor": "all-contributors", - "dev": "NODE_ENV=development yarn build && concurrently --kill-others --success first \"tsc -p tsconfig.json \" \"snowpack dev\" \"nodemon .\"", - "lint": "eslint --ext .ts,.js .", - "prepublishOnly": "NODE_ENV=production yarn build", + "build": "snowpack build", + "dev": "snowpack dev", + "prepublishOnly": "snowpack build", "start": "NODE_ENV=production node .", + "lint": "eslint --ext .ts,.js .", + "contributor": "all-contributors", "test": "mocha -r babel-register-ts src/**/*.spec.ts", - "postinstall": "snowpack install", - "clean": "rm -rf assets/css build lib web_modules node_modules" + "clean": "bash build.sh --clean" }, "repository": { "type": "git", @@ -75,7 +73,7 @@ [ "@snowpack/plugin-run-script", { - "cmd": "sass assets/scss:assets/css --load-path=node_modules -s compressed --no-source-map", + "cmd": "bash build.sh", "watch": "$1 --watch" } ] diff --git a/assets/favicon.ico b/src/assets/favicon.ico similarity index 100% rename from assets/favicon.ico rename to src/assets/favicon.ico diff --git a/assets/scss/options.scss b/src/assets/scss/options.scss similarity index 100% rename from assets/scss/options.scss rename to src/assets/scss/options.scss diff --git a/assets/scss/overlay.scss b/src/assets/scss/overlay.scss similarity index 100% rename from assets/scss/overlay.scss rename to src/assets/scss/overlay.scss diff --git a/assets/scss/styles.scss b/src/assets/scss/styles.scss similarity index 100% rename from assets/scss/styles.scss rename to src/assets/scss/styles.scss diff --git a/assets/scss/terminal.scss b/src/assets/scss/terminal.scss similarity index 100% rename from assets/scss/terminal.scss rename to src/assets/scss/terminal.scss diff --git a/assets/scss/variables.scss b/src/assets/scss/variables.scss similarity index 100% rename from assets/scss/variables.scss rename to src/assets/scss/variables.scss diff --git a/src/buffer.ts b/src/buffer.ts index 5818d51..bf65832 100644 --- a/src/buffer.ts +++ b/src/buffer.ts @@ -3,13 +3,13 @@ import { createInterface } from 'readline'; ask('Enter your username'); function ask(question: string): Promise { - const r = createInterface({ + const rlp = createInterface({ input: process.stdin, output: process.stdout, }); return new Promise(resolve => { - r.question(`${question}: `, answer => { - r.close(); + rlp.question(`${question}: `, answer => { + rlp.close(); resolve(answer); }); }); diff --git a/src/client/client/options.ts b/src/client/client/options.ts deleted file mode 100644 index 8a6d46f..0000000 --- a/src/client/client/options.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { isUndefined } from '../../web_modules/lodash.js'; - -export function loadOptions(): object { - const defaultOptions = { fontSize: 14 }; - try { - return isUndefined(localStorage.options) - ? defaultOptions - : JSON.parse(localStorage.options); - } catch { - return defaultOptions; - } -} diff --git a/src/client/dev.ts b/src/client/dev.ts new file mode 100644 index 0000000..831a33c --- /dev/null +++ b/src/client/dev.ts @@ -0,0 +1,5 @@ +caches.keys().then(cacheNames => { + cacheNames.forEach(cacheName => { + caches.delete(cacheName); + }); +}); diff --git a/src/client/index.ts b/src/client/index.ts deleted file mode 100644 index a1e7b41..0000000 --- a/src/client/index.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { Terminal } from '../web_modules/xterm.js'; -import { isNull } from '../web_modules/lodash.js'; -import { FitAddon } from '../web_modules/xterm-addon-fit.js'; -import { - dom, - library, -} from '../web_modules/@fortawesome/fontawesome-svg-core.js'; -import { faCogs } from '../web_modules/@fortawesome/free-solid-svg-icons.js'; -import Toastify from '../web_modules/toastify-js.js'; -import fileType from '../web_modules/file-type.js'; - -import { FileDownloader } from './client/download.js'; -import { copySelected, copyShortcut } from './client/copyToClipboard.js'; -import { disconnect } from './client/disconnect.js'; -import { loadOptions } from './client/options.js'; -import { mobileKeyboard } from './client/mobile.js'; -import { overlay, terminal } from './shared/elements.js'; -import { socket } from './client/socket.js'; -import { verifyPrompt } from './shared/verify.js'; - -// Setup for fontawesome -library.add(faCogs); -dom.watch(); - -socket.on('connect', () => { - const term = new Terminal(); - if (isNull(terminal)) return; - const fitAddon = new FitAddon(); - term.loadAddon(fitAddon); - term.open(terminal); - const resize = (): void => { - fitAddon.fit(); - socket.emit('resize', { cols: term.cols, rows: term.rows }); - }; - - const options = loadOptions(); - Object.entries(options).forEach(([key, value]) => { - term.setOption(key, value); - }); - const code = JSON.stringify(options, null, 2); - const editor = document.querySelector('#options .editor'); - if (!isNull(editor)) { - editor.value = code; - editor.addEventListener('keyup', () => { - try { - const updated = JSON.parse(editor.value); - const updatedCode = JSON.stringify(updated, null, 2); - editor.value = updatedCode; - editor.classList.remove('error'); - localStorage.options = updatedCode; - Object.keys(updated).forEach(key => { - const value = updated[key]; - term.setOption(key, value); - }); - resize(); - } catch { - // skip - editor.classList.add('error'); - } - }); - const toggle = document.querySelector('#options .toggler'); - const optionsElem = document.getElementById('options'); - if (!isNull(toggle) && !isNull(optionsElem)) { - toggle.addEventListener('click', e => { - optionsElem.classList.toggle('opened'); - e.preventDefault(); - }); - } - } - if (!isNull(overlay)) overlay.style.display = 'none'; - window.addEventListener('beforeunload', verifyPrompt, false); - - term.attachCustomKeyEventHandler(copyShortcut); - - document.addEventListener( - 'mouseup', - () => { - if (term.hasSelection()) copySelected(term.getSelection()); - }, - false, - ); - - window.onresize = resize; - resize(); - term.focus(); - mobileKeyboard(); - - const fileDownloader = new FileDownloader((bufferCharacters: string) => { - let fileCharacters = bufferCharacters; - // Try to decode it as base64, if it fails we assume it's not base64 - try { - fileCharacters = window.atob(fileCharacters); - } catch (err) { - // Assuming it's not base64... - } - - const bytes = new Uint8Array(fileCharacters.length); - for (let i = 0; i < fileCharacters.length; i += 1) { - bytes[i] = fileCharacters.charCodeAt(i); - } - - let mimeType = 'application/octet-stream'; - let fileExt = ''; - const typeData = fileType(bytes); - if (typeData) { - mimeType = typeData.mime; - fileExt = typeData.ext; - } - // Check if the buffer is ASCII - // Ref: https://stackoverflow.com/a/14313213 - // eslint-disable-next-line no-control-regex - else if (/^[\x00-\xFF]*$/.test(fileCharacters)) { - mimeType = 'text/plain'; - fileExt = 'txt'; - } - const fileName = `file-${new Date() - .toISOString() - .split('.')[0] - .replace(/-/g, '') - .replace('T', '') - .replace(/:/g, '')}${fileExt ? `.${fileExt}` : ''}`; - - const blob = new Blob([new Uint8Array(bytes.buffer)], { - type: mimeType, - }); - const blobUrl = URL.createObjectURL(blob); - - Toastify({ - text: `Download ready: ${fileName}`, - duration: 10000, - newWindow: true, - gravity: 'bottom', - position: 'right', - backgroundColor: '#fff', - stopOnFocus: true, - }).showToast(); - }); - - term.onData((data: string) => { - socket.emit('input', data); - }); - term.onResize((size: string) => { - socket.emit('resize', size); - }); - socket - .on('data', (data: string) => { - const remainingData = fileDownloader.buffer(data); - if (remainingData) { - term.write(remainingData); - } - }) - .on('login', () => { - term.writeln(''); - resize(); - }) - .on('logout', disconnect) - .on('disconnect', disconnect) - .on('error', (err: string | null) => { - if (err) disconnect(err); - }); -}); diff --git a/src/client/shared/elements.ts b/src/client/shared/elements.ts index 13f476a..e54a1cf 100644 --- a/src/client/shared/elements.ts +++ b/src/client/shared/elements.ts @@ -1,2 +1,5 @@ export const overlay = document.getElementById('overlay'); export const terminal = document.getElementById('terminal'); +export const editor = document.querySelector( + '#options .editor', +) as HTMLInputElement; diff --git a/src/client/wetty.ts b/src/client/wetty.ts new file mode 100644 index 0000000..0ed9b6b --- /dev/null +++ b/src/client/wetty.ts @@ -0,0 +1,74 @@ +import _ from 'lodash'; +import { Terminal } from 'xterm'; +import { FitAddon } from 'xterm-addon-fit'; +import { dom, library } from '@fortawesome/fontawesome-svg-core'; +import { faCogs } from '@fortawesome/free-solid-svg-icons'; + +import { FileDownloader } from './wetty/download.js'; +import { copySelected, copyShortcut } from './wetty/clipboard.js'; +import { disconnect } from './wetty/disconnect.js'; +import { configureTerm } from './wetty/options.js'; +import { mobileKeyboard } from './wetty/mobile.js'; +import { overlay, terminal } from './shared/elements.js'; +import { socket } from './wetty/socket.js'; +import { verifyPrompt } from './shared/verify.js'; + +// Setup for fontawesome +library.add(faCogs); +dom.watch(); + +socket.on('connect', () => { + const term = new Terminal(); + if (_.isNull(terminal)) return; + const fitAddon = new FitAddon(); + term.loadAddon(fitAddon); + term.open(terminal); + const resize = (): void => { + fitAddon.fit(); + socket.emit('resize', { cols: term.cols, rows: term.rows }); + }; + + configureTerm(term, resize); + + if (!_.isNull(overlay)) overlay.style.display = 'none'; + window.addEventListener('beforeunload', verifyPrompt, false); + + term.attachCustomKeyEventHandler(copyShortcut); + + document.addEventListener( + 'mouseup', + event => { + if (term.hasSelection()) copySelected(event, term.getSelection()); + }, + false, + ); + + window.onresize = resize; + resize(); + term.focus(); + mobileKeyboard(); + const fileDownloader = new FileDownloader(); + + term.onData((data: string) => { + socket.emit('input', data); + }); + term.onResize((size: { cols: number; rows: number }) => { + socket.emit('resize', size); + }); + socket + .on('data', (data: string) => { + const remainingData = fileDownloader.buffer(data); + if (remainingData) { + term.write(remainingData); + } + }) + .on('login', () => { + term.writeln(''); + resize(); + }) + .on('logout', disconnect) + .on('disconnect', disconnect) + .on('error', (err: string | null) => { + if (err) disconnect(err); + }); +}); diff --git a/src/client/client/copyToClipboard.ts b/src/client/wetty/clipboard.ts similarity index 66% rename from src/client/client/copyToClipboard.ts rename to src/client/wetty/clipboard.ts index efad698..4849efd 100644 --- a/src/client/client/copyToClipboard.ts +++ b/src/client/wetty/clipboard.ts @@ -1,7 +1,12 @@ -// NOTE text selection on double click or select -export function copySelected(text: string): boolean { - if (window.clipboardData?.setData) { - window.clipboardData.setData('Text', text); +/** + Copy text selection to clipboard on double click or select + @param event - the event this function is bound to eg mouseup + @param text - the selected text to copy + @returns boolean to indicate success or failure + */ +export function copySelected(event: Event, text: string): boolean { + if (event.clipboardData?.setData) { + event.clipboardData.setData('Text', text); return true; } if ( diff --git a/src/client/client/disconnect.ts b/src/client/wetty/disconnect.ts similarity index 64% rename from src/client/client/disconnect.ts rename to src/client/wetty/disconnect.ts index 8ce3113..72a6cef 100644 --- a/src/client/client/disconnect.ts +++ b/src/client/wetty/disconnect.ts @@ -1,11 +1,11 @@ -import { isNull, isUndefined } from '../../web_modules/lodash.js'; +import _ from 'lodash'; import { verifyPrompt } from '../shared/verify.js'; import { overlay } from '../shared/elements.js'; export function disconnect(reason: string): void { - if (isNull(overlay)) return; + if (_.isNull(overlay)) return; overlay.style.display = 'block'; const msg = document.getElementById('msg'); - if (!isUndefined(reason) && !isNull(msg)) msg.innerHTML = reason; + if (!_.isUndefined(reason) && !_.isNull(msg)) msg.innerHTML = reason; window.removeEventListener('beforeunload', verifyPrompt, false); } diff --git a/src/client/client/download.spec.ts b/src/client/wetty/download.spec.ts similarity index 100% rename from src/client/client/download.spec.ts rename to src/client/wetty/download.spec.ts diff --git a/src/client/client/download.ts b/src/client/wetty/download.ts similarity index 62% rename from src/client/client/download.ts rename to src/client/wetty/download.ts index 7db2064..0250486 100644 --- a/src/client/client/download.ts +++ b/src/client/wetty/download.ts @@ -1,6 +1,60 @@ +import Toastify from 'toastify-js'; +import fileType from 'file-type'; + const DEFAULT_FILE_BEGIN = '\u001b[5i'; const DEFAULT_FILE_END = '\u001b[4i'; +function onCompleteFile(bufferCharacters: string): void { + let fileCharacters = bufferCharacters; + // Try to decode it as base64, if it fails we assume it's not base64 + try { + fileCharacters = window.atob(fileCharacters); + } catch (err) { + // Assuming it's not base64... + } + + const bytes = new Uint8Array(fileCharacters.length); + for (let i = 0; i < fileCharacters.length; i += 1) { + bytes[i] = fileCharacters.charCodeAt(i); + } + + let mimeType = 'application/octet-stream'; + let fileExt = ''; + const typeData = fileType(bytes); + if (typeData) { + mimeType = typeData.mime; + fileExt = typeData.ext; + } + // Check if the buffer is ASCII + // Ref: https://stackoverflow.com/a/14313213 + // eslint-disable-next-line no-control-regex + else if (/^[\x00-\xFF]*$/.test(fileCharacters)) { + mimeType = 'text/plain'; + fileExt = 'txt'; + } + const fileName = `file-${new Date() + .toISOString() + .split('.')[0] + .replace(/-/g, '') + .replace('T', '') + .replace(/:/g, '')}${fileExt ? `.${fileExt}` : ''}`; + + const blob = new Blob([new Uint8Array(bytes.buffer)], { + type: mimeType, + }); + const blobUrl = URL.createObjectURL(blob); + + Toastify({ + text: `Download ready: ${fileName}`, + duration: 10000, + newWindow: true, + gravity: 'bottom', + position: 'right', + backgroundColor: '#fff', + stopOnFocus: true, + }).showToast(); +} + export class FileDownloader { fileBuffer: string[]; fileBegin: string; @@ -9,15 +63,15 @@ export class FileDownloader { onCompleteFileCallback: Function; constructor( - onCompleteFileCallback: (file: string) => void, + onCompleteFileCallback: Function = onCompleteFile, fileBegin: string = DEFAULT_FILE_BEGIN, - fileEnd: string = DEFAULT_FILE_END + fileEnd: string = DEFAULT_FILE_END, ) { this.fileBuffer = []; - this.onCompleteFileCallback = onCompleteFileCallback; this.fileBegin = fileBegin; this.fileEnd = fileEnd; this.partialFileBegin = ''; + this.onCompleteFileCallback = onCompleteFileCallback; } bufferCharacter(character: string): string { @@ -69,13 +123,13 @@ export class FileDownloader { this.fileBuffer.length >= this.fileBegin.length + this.fileEnd.length && this.fileBuffer.slice(-this.fileEnd.length).join('') === this.fileEnd ) { - this.onCompleteFile( + this.onCompleteFileCallback( this.fileBuffer .slice( this.fileBegin.length, - this.fileBuffer.length - this.fileEnd.length + this.fileBuffer.length - this.fileEnd.length, ) - .join('') + .join(''), ); this.fileBuffer = []; } @@ -93,13 +147,6 @@ export class FileDownloader { ) { return data; } - return data - .split('') - .map(this.bufferCharacter.bind(this)) - .join(''); - } - - onCompleteFile(bufferCharacters: string): void { - this.onCompleteFileCallback(bufferCharacters); + return data.split('').map(this.bufferCharacter.bind(this)).join(''); } } diff --git a/src/client/client/mobile.ts b/src/client/wetty/mobile.ts similarity index 83% rename from src/client/client/mobile.ts rename to src/client/wetty/mobile.ts index 671092c..014c791 100644 --- a/src/client/client/mobile.ts +++ b/src/client/wetty/mobile.ts @@ -1,8 +1,8 @@ -import { isNull } from '../../web_modules/lodash.js'; +import _ from 'lodash'; export function mobileKeyboard(): void { const [screen] = document.getElementsByClassName('xterm-screen'); - if (isNull(screen)) return; + if (_.isNull(screen)) return; screen.setAttribute('contenteditable', 'true'); screen.setAttribute('spellcheck', 'false'); screen.setAttribute('autocorrect', 'false'); diff --git a/src/client/wetty/options.ts b/src/client/wetty/options.ts new file mode 100644 index 0000000..2a0c8e7 --- /dev/null +++ b/src/client/wetty/options.ts @@ -0,0 +1,51 @@ +import _ from 'lodash'; +import type { Terminal } from 'xterm'; + +import { editor } from '../shared/elements.js'; + +function loadOptions(): object { + const defaultOptions = { fontSize: 14 }; + try { + return _.isUndefined(localStorage.options) + ? defaultOptions + : JSON.parse(localStorage.options); + } catch { + return defaultOptions; + } +} + +export function configureTerm(term: Terminal, resize: Function): void { + const options = loadOptions(); + Object.entries(options).forEach(([key, value]) => { + term.setOption(key, value); + }); + const config = JSON.stringify(options, null, 2); + if (!_.isNull(editor)) { + editor.value = config; + editor.addEventListener('keyup', () => { + try { + const updated = JSON.parse(editor.value); + const updatedConf = JSON.stringify(updated, null, 2); + editor.value = updatedConf; + editor.classList.remove('error'); + localStorage.options = updatedConf; + Object.keys(updated).forEach(key => { + const value = updated[key]; + term.setOption(key, value); + }); + resize(); + } catch { + // skip + editor.classList.add('error'); + } + }); + const toggle = document.querySelector('#options .toggler'); + const optionsElem = document.getElementById('options'); + if (!_.isNull(toggle) && !_.isNull(optionsElem)) { + toggle.addEventListener('click', e => { + optionsElem.classList.toggle('opened'); + e.preventDefault(); + }); + } + } +} diff --git a/src/client/client/socket.ts b/src/client/wetty/socket.ts similarity index 83% rename from src/client/client/socket.ts rename to src/client/wetty/socket.ts index 5341c35..4195c8d 100644 --- a/src/client/client/socket.ts +++ b/src/client/wetty/socket.ts @@ -1,4 +1,4 @@ -import io from '../../web_modules/socket.io-client.js'; +import io from 'socket.io-client'; const userRegex = new RegExp('ssh/[^/]+$'); export const trim = (str: string): string => str.replace(/\/*$/, ''); diff --git a/src/server.ts b/src/server.ts index 2f11e6a..4a32ff1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -19,6 +19,7 @@ import { /** * Starts WeTTy Server * @name startServer + * @returns Promise that resolves SocketIO server */ export async function startServer( ssh: SSH = sshDefault, diff --git a/src/server/socketServer.ts b/src/server/socketServer.ts index c232dca..a58a58e 100644 --- a/src/server/socketServer.ts +++ b/src/server/socketServer.ts @@ -5,7 +5,6 @@ import helmet from 'helmet'; import http from 'http'; import https from 'https'; import isUndefined from 'lodash/isUndefined.js'; -import sassMiddleware from 'node-sass-middleware'; import socket from 'socket.io'; import winston from 'express-winston'; import { join, resolve } from 'path'; @@ -15,6 +14,8 @@ import { html } from './socketServer/html.js'; import { logger } from '../shared/logger.js'; const trim = (str: string): string => str.replace(/\/*$/, ''); +const serveStatic = (path: string) => + express.static(resolve(process.cwd(), 'build', path)); export function server( { base, port, host, title, bypassHelmet }: Server, @@ -32,28 +33,12 @@ export function server( const app = express(); app - .use( - `${basePath}/web_modules`, - express.static(resolve(process.cwd(), 'web_modules')), - ) - .use( - sassMiddleware({ - src: resolve(process.cwd(), 'lib', 'client'), - dest: resolve(process.cwd(), 'assets'), - outputStyle: 'compressed', - log(severity: string, key: string, value: string) { - logger.log(severity, 'node-sass-middleware %s : %s', key, value); - }, - }), - ) - .use(`${basePath}/assets`, express.static(resolve(process.cwd(), 'assets'))) - .use( - `${basePath}/client`, - express.static(resolve(process.cwd(), 'lib', 'client')), - ) + .use(`${basePath}/web_modules`, serveStatic('web_modules')) + .use(`${basePath}/assets`, serveStatic('assets')) + .use(`${basePath}/client`, serveStatic('client')) .use(winston.logger(logger)) .use(compression()) - .use(favicon(join('assets', 'favicon.ico'))); + .use(favicon(join('build', 'assets', 'favicon.ico'))); /* .use((req, res, next) => { if (req.path.substr(-1) === '/' && req.path.length > 1) res.redirect( diff --git a/src/server/socketServer/html.ts b/src/server/socketServer/html.ts index 219f677..47785ed 100644 --- a/src/server/socketServer/html.ts +++ b/src/server/socketServer/html.ts @@ -1,9 +1,13 @@ -import express from 'express'; +import type express from 'express'; +import { isDev } from '../../shared/env.js'; + +const jsFiles = isDev ? ['dev', 'wetty'] : ['wetty']; +const cssFiles = ['styles', 'options', 'overlay', 'terminal']; const render = ( title: string, - css: string, - js: string, + css: string[], + js: string[], ): string => ` @@ -11,7 +15,7 @@ const render = ( ${title} - + ${css.map(file => ``).join('\n')}
@@ -24,11 +28,13 @@ const render = ( + >
- `) + .join('\n')} `; @@ -37,5 +43,9 @@ export const html = (base: string, title: string) => ( res: express.Response, ) => res.send( - render(title, `${base}/assets/styles.css`, `${base}/client/index.js`), + render( + title, + cssFiles.map(css => `${base}/assets/css/${css}.css`), + jsFiles.map(js => `${base}/client/${js}.js`), + ), ); diff --git a/src/shared/env.ts b/src/shared/env.ts new file mode 100644 index 0000000..0bd4ecf --- /dev/null +++ b/src/shared/env.ts @@ -0,0 +1 @@ +export const isDev = process.env.NODE_ENV === 'development'; diff --git a/src/shared/logger.ts b/src/shared/logger.ts index 6419b18..6cf2c62 100644 --- a/src/shared/logger.ts +++ b/src/shared/logger.ts @@ -1,5 +1,7 @@ import winston from 'winston'; +import { isDev } from './env.js'; + const { combine, timestamp, label, simple, json, colorize } = winston.format; const dev = combine( @@ -12,10 +14,10 @@ const dev = combine( const prod = combine(label({ label: 'Wetty' }), timestamp(), json()); export const logger = winston.createLogger({ - format: process.env.NODE_ENV === 'development' ? dev : prod, + format: isDev ? dev : prod, transports: [ new winston.transports.Console({ - level: process.env.NODE_ENV === 'development' ? 'debug' : 'info', + level: isDev ? 'debug' : 'info', handleExceptions: true, }), ], diff --git a/tsconfig.json b/tsconfig.json index 32f5286..fb9a761 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "noImplicitThis": true, "noUnusedLocals": true, "noUnusedParameters": true, - "outDir": "./lib", + "outDir": "./build", "removeComments": true, "skipLibCheck": true, "sourceMap": true,