Skip to content

Commit f9767e4

Browse files
committedJul 3, 2020
refactor(server): add and start enforcing stricter ts config (cherry-pick)
Cherry-picked from refactor/stricter-typechecking (PR redpwn#303). This commit contains all changes that do not break the lint job.
1 parent b367340 commit f9767e4

File tree

13 files changed

+190
-113
lines changed

13 files changed

+190
-113
lines changed
 

‎.eslintrc.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ module.exports = {
2020
maxBOF: 0
2121
}],
2222
'padding-line-between-statements': ['error',
23-
{blankLine: 'always', prev: 'block-like', next: 'export'}
24-
]
23+
{ blankLine: 'always', prev: 'block-like', next: 'export' }
24+
],
25+
'no-void': ['error', {
26+
allowAsStatement: true
27+
}]
2528
}
2629
}

‎package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"license": "MIT",
55
"private": true,
66
"scripts": {
7-
"lint": "tsc --noEmit && eslint --ext .js,.ts .",
7+
"lint": "tsc --noEmit ; eslint .",
8+
"lint:strict": "tsc --noEmit -p tsconfig.strict.json ; eslint .",
89
"start": "node --enable-source-maps dist/server/index.js",
910
"migrate": "sh -c \"yarn build:ts && RCTF_DATABASE_MIGRATE=only yarn start\"",
1011
"build:client": "preact build --src client/src --template client/index.html --dest dist/build --no-prerender --no-inline-css",
@@ -58,8 +59,8 @@
5859
"devDependencies": {
5960
"@types/content-disposition": "0.5.3",
6061
"@types/nodemailer": "6.4.0",
61-
"@typescript-eslint/eslint-plugin": "2.34.0",
62-
"@typescript-eslint/parser": "2.34.0",
62+
"@typescript-eslint/eslint-plugin": "3.0.2",
63+
"@typescript-eslint/parser": "3.0.2",
6364
"ava": "3.9.0",
6465
"babel-eslint": "10.1.0",
6566
"babel-plugin-transform-export-extensions": "6.22.0",
@@ -69,7 +70,7 @@
6970
"cpy-cli": "3.1.1",
7071
"cross-env": "7.0.2",
7172
"cz-conventional-changelog": "3.2.0",
72-
"eslint": "6.8.0",
73+
"eslint": "7.1.0",
7374
"eslint-config-preact": "1.1.1",
7475
"eslint-config-standard": "14.1.1",
7576
"eslint-plugin-ava": "10.3.1",

‎server/.eslintrc.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const path = require('path')
2+
13
module.exports = {
24
env: {
35
node: true
@@ -7,14 +9,20 @@ module.exports = {
79
rules: {
810
},
911
parser: '@typescript-eslint/parser',
12+
parserOptions: {
13+
tsconfigRootDir: path.dirname(__dirname),
14+
project: ['./tsconfig.json']
15+
},
1016
overrides: [{
1117
files: ['*.ts'],
1218
extends: [
13-
'plugin:@typescript-eslint/eslint-recommended',
14-
'plugin:@typescript-eslint/recommended',
19+
'plugin:@typescript-eslint/recommended'
1520
],
1621
plugins: [
1722
'@typescript-eslint'
1823
]
24+
}, {
25+
files: ['.eslintrc.js'],
26+
parser: 'espree'
1927
}]
2028
}

‎server/challenges/Provider.ts

+4
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ export interface Provider extends EventEmitter {
1313
deleteChallenge(id: string): void;
1414
cleanup (): void;
1515
}
16+
17+
export interface ProviderConstructor {
18+
new (options: unknown): Provider
19+
}

‎server/challenges/index.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import config from '../../config/server'
22
import path from 'path'
33
import { Challenge, CleanedChallenge } from './types'
4-
import { Provider } from './Provider'
4+
import { Provider, ProviderConstructor } from './Provider'
55
import { challUpdateEmitter, publishChallUpdate } from '../cache/challs'
66

77
let provider: Provider
88

99
let challenges: Challenge[] = []
1010
let cleanedChallenges: CleanedChallenge[] = []
1111

12-
let challengesMap: Map<string, Challenge> = new Map()
13-
let cleanedChallengesMap: Map<string, CleanedChallenge> = new Map()
12+
let challengesMap = new Map<string, Challenge>()
13+
let cleanedChallengesMap = new Map<string, CleanedChallenge>()
1414

1515
const cleanChallenge = (chall: Challenge): CleanedChallenge => {
1616
const { files, description, author, points, id, name, category, sortWeight } = chall
@@ -34,8 +34,8 @@ const onUpdate = (newChallenges: Challenge[]): void => {
3434
cleanedChallengesMap = new Map(cleanedChallenges.map(c => [c.id, c]))
3535
}
3636

37-
import(path.join('../providers', config.challengeProvider.name))
38-
.then(({ default: Provider }) => {
37+
void import(path.join('../providers', config.challengeProvider.name))
38+
.then(({ default: Provider }: { default: ProviderConstructor }) => {
3939
provider = new Provider(config.challengeProvider.options)
4040

4141
provider.on('update', onUpdate)

‎server/providers/challenges/database/index.ts

+17-17
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as db from '../../../database'
77
import { deepCopy } from '../../../util'
88

99
interface DatabaseProviderOptions {
10-
updateInterval: number;
10+
updateInterval?: number;
1111
}
1212

1313
interface DatabaseChallenge {
@@ -16,25 +16,25 @@ interface DatabaseChallenge {
1616
}
1717

1818
class DatabaseProvider extends EventEmitter implements Provider {
19-
private _updateInterval: number
20-
private _interval: NodeJS.Timeout
21-
private challenges: Challenge[]
19+
private updateInterval: number
20+
private interval: NodeJS.Timeout
21+
private challenges: Challenge[] = []
2222

23-
constructor (options: DatabaseProviderOptions) {
23+
constructor (_options: DatabaseProviderOptions) {
2424
super()
25-
options = {
25+
const options: Required<DatabaseProviderOptions> = {
2626
updateInterval: 60 * 1000,
27-
...options
27+
..._options
2828
}
2929

30-
this._updateInterval = options.updateInterval
31-
this._interval = setInterval(() => this._update(), this._updateInterval)
32-
this._update()
30+
this.updateInterval = options.updateInterval
31+
this.interval = setInterval(() => { void this.update() }, this.updateInterval)
32+
void this.update()
3333
}
3434

35-
async _update (): Promise<void> {
35+
private async update (): Promise<void> {
3636
try {
37-
const dbchallenges: DatabaseChallenge[] = await db.challenges.getAllChallenges()
37+
const dbchallenges = await db.challenges.getAllChallenges() as DatabaseChallenge[]
3838

3939
this.challenges = dbchallenges.map(({ id, data }) => {
4040
return {
@@ -51,7 +51,7 @@ class DatabaseProvider extends EventEmitter implements Provider {
5151
}
5252

5353
forceUpdate (): void {
54-
this._update()
54+
void this.update()
5555
}
5656

5757
challengeToRow (chall: Challenge): DatabaseChallenge {
@@ -69,7 +69,7 @@ class DatabaseProvider extends EventEmitter implements Provider {
6969
async updateChallenge (chall: Challenge): Promise<void> {
7070
const originalData = await db.challenges.getChallengeById({
7171
id: chall.id
72-
})
72+
}) as DatabaseChallenge
7373

7474
// If we're inserting, have sane defaults
7575
if (originalData === undefined) {
@@ -85,17 +85,17 @@ class DatabaseProvider extends EventEmitter implements Provider {
8585

8686
await db.challenges.upsertChallenge(data)
8787

88-
this._update()
88+
void this.update()
8989
}
9090

9191
async deleteChallenge (id: string): Promise<void> {
9292
await db.challenges.removeChallengeById({ id: id })
9393

94-
this._update()
94+
void this.update()
9595
}
9696

9797
cleanup (): void {
98-
clearInterval(this._interval)
98+
clearInterval(this.interval)
9999
}
100100
}
101101

‎server/providers/challenges/rdeploy-blob/index.ts

+24-20
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,29 @@ interface RDeployChallenge {
2929
}
3030

3131
class RDeployBlobProvider extends EventEmitter implements Provider {
32-
private _updateInterval: number
33-
private _rDeployDirectory: string
34-
private _interval: NodeJS.Timeout
35-
private challenges: Challenge[]
32+
private updateInterval: number
33+
private rDeployDirectory: string
34+
private interval: NodeJS.Timeout
35+
private challenges: Challenge[] = []
3636

3737
private ready = false
3838
private downloadMap: Map<string, string>
3939

40-
constructor (options: RDeployBlobProviderOptions) {
40+
constructor (_options: RDeployBlobProviderOptions) {
4141
super()
42-
options = {
42+
const options: Required<RDeployBlobProviderOptions> = {
4343
updateInterval: 60 * 1000,
44-
...options
44+
..._options
4545
}
4646

47-
this._updateInterval = options.updateInterval
48-
this._rDeployDirectory = path.join(__dirname, '../../../../../', options.rDeployDirectory)
49-
this._interval = setInterval(() => this._update(), this._updateInterval)
47+
this.updateInterval = options.updateInterval
48+
this.rDeployDirectory = path.join(__dirname, '../../../../../', options.rDeployDirectory)
49+
this.interval = setInterval(() => this.update(), this.updateInterval)
5050

51-
this.downloadMap = new Map()
51+
this.downloadMap = new Map<string, string>()
5252

53-
const fileDir = path.join(this._rDeployDirectory, options.rDeployFiles)
54-
fs.readdir(fileDir)
53+
const fileDir = path.join(this.rDeployDirectory, options.rDeployFiles)
54+
void fs.readdir(fileDir)
5555
.then(async files => {
5656
await Promise.all(files.map(async file => {
5757
const filePath = path.join(fileDir, file)
@@ -68,25 +68,29 @@ class RDeployBlobProvider extends EventEmitter implements Provider {
6868

6969
// When done uploading files, allow updates
7070
this.ready = true
71-
this._update()
71+
this.update()
7272
})
7373
}
7474

75-
_update (): void {
75+
private update (): void {
7676
// Prevent updates if downloads not initialized
7777
if (!this.ready) return
7878

79-
fs.readFile(path.join(this._rDeployDirectory, 'config.json'), 'utf8')
79+
fs.readFile(path.join(this.rDeployDirectory, 'config.json'), 'utf8')
8080
.then((data: string) => {
8181
try {
82-
const rawChallenges: RDeployChallenge[] = JSON.parse(data)
82+
const rawChallenges = JSON.parse(data) as RDeployChallenge[]
8383

8484
this.challenges = rawChallenges.map((chall: RDeployChallenge): Challenge => {
8585
const downloadUrls: File[] = chall.files.map(file => {
8686
const basename = path.basename(file)
87+
const fileUrl = this.downloadMap.get(basename)
88+
if (fileUrl === undefined) {
89+
throw new Error(`File not found: ${basename}`)
90+
}
8791
return {
8892
name: normalize.normalizeDownload(basename),
89-
url: this.downloadMap.get(basename)
93+
url: fileUrl
9094
}
9195
})
9296

@@ -109,7 +113,7 @@ class RDeployBlobProvider extends EventEmitter implements Provider {
109113
}
110114

111115
forceUpdate (): void {
112-
this._update()
116+
this.update()
113117
}
114118

115119
updateChallenge (chall: Challenge): void {
@@ -135,7 +139,7 @@ class RDeployBlobProvider extends EventEmitter implements Provider {
135139
}
136140

137141
cleanup (): void {
138-
clearInterval(this._interval)
142+
clearInterval(this.interval)
139143
}
140144
}
141145

‎server/providers/uploads/gcs/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import crypto from 'crypto'
33
import { Provider } from '../../../uploads/types'
44

55
interface GcsProviderOptions {
6-
credentials: object;
6+
credentials: Record<string, unknown>;
77
bucketName: string;
88
}
99

‎server/uploads/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Provider } from './types'
44
import fastify from 'fastify'
55
import { Server, IncomingMessage, ServerResponse } from 'http'
66

7-
let provider: Provider = null
7+
let provider: Provider | null = null
88

99
export const init = (app: fastify.FastifyInstance<Server, IncomingMessage, ServerResponse> | null): void => {
1010
const name = app === null ? 'uploads/dummy' : config.uploadProvider.name

‎server/util/index.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ export * as normalize from './normalize'
88
export * as validate from './validate'
99
export * as scores from './scores'
1010

11-
// This function does not work for non JSON stringifiable objects
11+
/**
12+
* Perform a deep-copy of a JSON-stringifiable object
13+
*
14+
* @template T
15+
* @param {T} data data to copy
16+
* @returns {T} deep copy of data
17+
*/
1218
export const deepCopy = data => {
1319
return JSON.parse(JSON.stringify(data))
1420
}

‎tsconfig.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"compilerOptions": {
33
"module": "commonjs",
4+
"target": "es2019",
5+
"moduleResolution": "node",
6+
"noImplicitAny": true,
7+
"allowJs": true,
48
"esModuleInterop": true,
59
"allowSyntheticDefaultImports": true,
6-
"target": "es6",
7-
"noImplicitAny": true,
8-
"moduleResolution": "node",
910
"inlineSourceMap": true,
1011
"inlineSources": true,
11-
"allowJs": true,
1212
"outDir": "dist",
1313
"baseUrl": "."
1414
},

‎tsconfig.strict.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"strict": true,
5+
"noImplicitAny": false,
6+
"checkJs": true,
7+
}
8+
}

0 commit comments

Comments
 (0)
Please sign in to comment.