diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..cdf57c6 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,114 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "parserOptions": { + "project": [ + "./tsconfig.json", + "./tsconfig.test.json", + "./tsconfig.frontend.json" + ] + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "indent": [ + "error", + 4, + { + "SwitchCase": 1 + } + ], + "no-trailing-spaces": "error", + "max-len": [ + "error", + { + "code": 120, + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreRegExpLiterals": true + } + ], + "semi": "off", + "@typescript-eslint/semi": [ + "error" + ], + "no-extra-semi": "error", + "eol-last": "error", + "comma-dangle": "off", + "@typescript-eslint/comma-dangle": [ + "error", + { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "always-multiline", + "enums": "always-multiline", + "generics": "always-multiline", + "tuples": "always-multiline" + } + ], + "no-extra-parens": "off", + "@typescript-eslint/no-extra-parens": [ + "error" + ], + "no-nested-ternary": "error", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/explicit-module-boundary-types": "error", + "@typescript-eslint/no-unnecessary-condition": "error", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_" + } + ], + "@typescript-eslint/no-non-null-assertion": "error", + "no-useless-return": "error", + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": [ + "error" + ], + "no-return-await": "off", + "@typescript-eslint/return-await": [ + "error", + "always" + ], + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { + "accessibility": "explicit" + } + ], + "@typescript-eslint/no-floating-promises": "error" + }, + "ignorePatterns": [ + "jest.config.js", + "webpack.config.js", + "dist/**/*", + "public/**/*", + "config/**/*" + ], + "overrides": [ + { + "files": [ + "test/**/*" + ], + "rules": { + "max-len": [ + "error", + { + "code": 120, + "ignoreTemplateLiterals": true, + "ignoreRegExpLiterals": true, + "ignoreStrings": true + } + ] + } + } + ] +} diff --git a/assets/ts/PersistentWebSocket.ts b/assets/ts/PersistentWebSocket.ts index b53f948..98e474f 100644 --- a/assets/ts/PersistentWebSocket.ts +++ b/assets/ts/PersistentWebSocket.ts @@ -8,13 +8,13 @@ export default class PersistentWebsocket { ) { } - public run() { - this.webSocket = new WebSocket(this.url); - this.webSocket.addEventListener('open', (e) => { + public run(): void { + const _webSocket = this.webSocket = new WebSocket(this.url); + this.webSocket.addEventListener('open', () => { console.debug('Websocket connected'); }); this.webSocket.addEventListener('message', (e) => { - this.handler(this.webSocket!, e); + this.handler(_webSocket, e); }); this.webSocket.addEventListener('error', (e) => { console.error('Websocket error', e); @@ -29,7 +29,7 @@ export default class PersistentWebsocket { }); } - public send(data: string) { + public send(data: string): void { if (!this.webSocket) throw new Error('WebSocket not connected'); this.webSocket.send(data); diff --git a/assets/ts/forms.ts b/assets/ts/forms.ts index 46cd108..d08a65e 100644 --- a/assets/ts/forms.ts +++ b/assets/ts/forms.ts @@ -1,7 +1,9 @@ /* * For labels to update their state (css selectors based on the value attribute) */ -export function updateInputs() { +import {ValidationError} from "wms-core/db/Validator"; + +export function updateInputs(): void { document.querySelectorAll('input, textarea').forEach(el => { if (!el.dataset.inputSetup) { el.dataset.inputSetup = 'true'; @@ -19,7 +21,10 @@ document.addEventListener('DOMContentLoaded', () => { updateInputs(); }); -export function applyFormMessages(formElement: HTMLFormElement, messages: { [p: string]: any }) { +export function applyFormMessages( + formElement: HTMLFormElement, + messages: { [p: string]: ValidationError }, +): void { for (const fieldName of Object.keys(messages)) { const field = formElement.querySelector('#field-' + fieldName); if (!field) continue; @@ -27,14 +32,12 @@ export function applyFormMessages(formElement: HTMLFormElement, messages: { [p: let parent = field.parentElement; while (parent && !parent.classList.contains('form-field')) parent = parent.parentElement; - if (field) { - let err = field.querySelector('.error'); - if (!err) { - err = document.createElement('div'); - err.classList.add('error'); - parent?.insertBefore(err, parent.querySelector('.hint') || parent); - } - err.innerHTML = ` ${messages[fieldName].message}`; + let err = field.querySelector('.error'); + if (!err) { + err = document.createElement('div'); + err.classList.add('error'); + parent?.insertBefore(err, parent.querySelector('.hint') || parent); } + err.innerHTML = ` ${messages[fieldName].message}`; } } diff --git a/assets/ts/tooltips-and-dropdowns.ts b/assets/ts/tooltips-and-dropdowns.ts index e66c732..069b405 100644 --- a/assets/ts/tooltips-and-dropdowns.ts +++ b/assets/ts/tooltips-and-dropdowns.ts @@ -1,4 +1,4 @@ -export function updateTooltips() { +export function updateTooltips(): void { console.debug('Updating tooltips'); const elements = document.querySelectorAll('.tip, .dropdown'); diff --git a/package.json b/package.json index 4c917b4..04056cc 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,12 @@ "scripts": { "test": "jest --verbose --runInBand", "dist-webpack": "webpack --mode production", - "dist": "(test ! -d dist || rm -r dist) && tsc && npm run dist-webpack", + "dist": "yarn compile && yarn dist-webpack", + "clean": "(test ! -d dist || rm -r dist)", + "compile": "yarn clean && tsc && mv dist/src/* dist/", "dev": "concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", - "start": "yarn dist && node dist/main.js" + "start": "yarn dist && node dist/main.js", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, "devDependencies": { "@babel/core": "^7.9.0", @@ -28,9 +31,12 @@ "@types/nodemailer": "^6.4.0", "@types/nunjucks": "^3.1.3", "@types/ws": "^7.2.6", + "@typescript-eslint/eslint-plugin": "^4.3.0", + "@typescript-eslint/parser": "^4.3.0", "babel-loader": "^8.1.0", "concurrently": "^5.1.0", "css-loader": "^4.2.2", + "eslint": "^7.10.0", "feather-icons": "^4.28.0", "file-loader": "^6.0.0", "imagemin": "^7.0.1", @@ -55,6 +61,6 @@ "dependencies": { "config": "^3.3.1", "express": "^4.17.1", - "wms-core": "^0.21.7" + "wms-core": "^0.22.0-rc.24" } } diff --git a/src/App.ts b/src/App.ts index 713f51b..c8dac6b 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1,6 +1,5 @@ import Application from "wms-core/Application"; -import {Type} from "wms-core/Utils"; -import Migration from "wms-core/db/Migration"; +import Migration, {MigrationType} from "wms-core/db/Migration"; import CreateMigrationsTable from "wms-core/migrations/CreateMigrationsTable"; import CreateLogsTable from "wms-core/migrations/CreateLogsTable"; import ExpressAppComponent from "wms-core/components/ExpressAppComponent"; @@ -18,16 +17,17 @@ import CsrfProtectionComponent from "wms-core/components/CsrfProtectionComponent import WebSocketServerComponent from "wms-core/components/WebSocketServerComponent"; import HomeController from "./controllers/HomeController"; import AutoUpdateComponent from "wms-core/components/AutoUpdateComponent"; +import packageJson = require('../package.json'); export default class App extends Application { - private readonly port: number; - - constructor(port: number) { - super(require('../package.json').version); - this.port = port; + public constructor( + private readonly addr: string, + private readonly port: number, + ) { + super(packageJson.version); } - protected getMigrations(): Type[] { + protected getMigrations(): MigrationType[] { return [ CreateMigrationsTable, CreateLogsTable, @@ -44,7 +44,7 @@ export default class App extends Application { const redisComponent = new RedisComponent(); const mysqlComponent = new MysqlComponent(); - const expressAppComponent = new ExpressAppComponent(this.port); + const expressAppComponent = new ExpressAppComponent(this.addr, this.port); this.use(expressAppComponent); this.use(new NunjucksComponent()); this.use(new LogRequestsComponent()); @@ -79,9 +79,10 @@ export default class App extends Application { } private registerWebSocketListeners() { + // WebSocket listeners } private registerControllers() { this.use(new HomeController()); } -} \ No newline at end of file +} diff --git a/src/controllers/HomeController.ts b/src/controllers/HomeController.ts index ad035b4..e6e3fb3 100644 --- a/src/controllers/HomeController.ts +++ b/src/controllers/HomeController.ts @@ -2,24 +2,24 @@ import Controller from "wms-core/Controller"; import {Request, Response} from "express"; export default class HomeController extends Controller { - routes(): void { + public routes(): void { this.get('/', this.getHome, 'home'); this.get('/about', this.getAbout, 'about'); this.get('/back', this.goBack, 'about'); } - private async getHome(req: Request, res: Response) { + protected async getHome(req: Request, res: Response): Promise { res.render('home'); } - private async getAbout(req: Request, res: Response) { + protected async getAbout(req: Request, res: Response): Promise { res.render('about'); } /** * This is to test and assert that wms-core extended types are available */ - private async goBack(req: Request, res: Response) { + protected async goBack(req: Request, res: Response): Promise { res.redirectBack(); } -} \ No newline at end of file +} diff --git a/src/main.ts b/src/main.ts index b3f32d5..76e7561 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,9 @@ +import {delimiter} from "path"; + // Load config from specified path or default + wms-core/config (default defaults) process.env['NODE_CONFIG_DIR'] = __dirname + '/../node_modules/wms-core/config/' - + require('path').delimiter + + delimiter + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../config/'); import Logger from "wms-core/Logger"; @@ -11,8 +13,8 @@ import config from "config"; (async () => { Logger.debug('Config path:', process.env['NODE_CONFIG_DIR']); - const app = new App(config.get('port')); + const app = new App(config.get('listen_addr'), config.get('port')); await app.start(); })().catch(err => { Logger.error(err); -}); \ No newline at end of file +}); diff --git a/test/App.test.ts b/test/App.test.ts index 451f0fb..d116a22 100644 --- a/test/App.test.ts +++ b/test/App.test.ts @@ -2,4 +2,4 @@ describe('Write your tests', () => { test('Remove this when you have some tests', () => { expect(false).toBe(true); }); -}); \ No newline at end of file +}); diff --git a/tsconfig.json b/tsconfig.json index 19d375e..5ea85f7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,10 +11,11 @@ ], "typeRoots": [ "./node_modules/@types" - ] + ], + "resolveJsonModule": true }, "include": [ "src/**/*", "node_modules/wms-core/types" ] -} \ No newline at end of file +} diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..897a6b4 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "typeRoots": [ + "node_modules/@types", + "src/types", + "test/types" + ] + }, + "include": [ + "src/types/**/*", + "test/**/*" + ] +} \ No newline at end of file