Skip to content

Commit 460bbe1

Browse files
wjhsfcolincasey
andauthored
Enable strict type checked rules (#392)
* Make usage of null/undefined more consistent. Primarily allowing either value for user input. * add explicit return type to canonicalDomain * add note for weird parse behavior * use @typescript-eslint/strict-type-checked * eslint auto fixes * remove unnecessary polyfill * manual fixes for strict rules * Update lib/cookie/cookie.ts Co-authored-by: Colin Casey <[email protected]> * Replace `Nullable<T>` with more precise `T | undefined` * add a bit of breathing room to the file size * Change `parse` to only return undefined on failure, nut null | undefined. * standardize helpers on returning undefined * update doc for return type * Fix a few more errors. * appease linter * change fromJSON to return undefined instead of null * fix incorrect comparison * change date helpers to avoid null, for consistency * update fromJSON tests for new return type * change internal null value to undefined * add type annotations * move from .eslintrc to flat config * linter fixes for vows tests * fixes for restrict-template-expressions * Avoid type linting eslint.config.mjs * fix package-lock.json * bump deps * fix package-lock.json again * restore missing dependency * revert changes to test files * disable no-unused-vars lint rule * fix no-case-declarations violation * remove max-lines enforcement we've decided it doesn't really add value * fix types and disable false positive eslint errors * fix package-lock.json I always mangle it :( * don't need to disable a rule that's never enabled missed a spot --------- Co-authored-by: Colin Casey <[email protected]>
1 parent 4e04082 commit 460bbe1

17 files changed

+1844
-8680
lines changed

.eslintignore

-4
This file was deleted.

.eslintrc.json

-19
This file was deleted.

eslint.config.mjs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// @ts-check
2+
3+
import eslint from '@eslint/js'
4+
import prettierRecommended from 'eslint-plugin-prettier/recommended'
5+
import tseslint from 'typescript-eslint'
6+
import globals from 'globals'
7+
8+
export default tseslint.config(
9+
{
10+
ignores: ['dist', 'jest.config.ts'],
11+
},
12+
eslint.configs.recommended,
13+
...tseslint.configs.strictTypeChecked,
14+
prettierRecommended,
15+
{
16+
languageOptions: {
17+
globals: globals.node,
18+
parserOptions: {
19+
project: './tsconfig.json',
20+
tsconfigDirName: import.meta.dirname,
21+
},
22+
},
23+
linterOptions: {
24+
reportUnusedDisableDirectives: 'warn',
25+
},
26+
rules: {
27+
'@typescript-eslint/explicit-function-return-type': 'error',
28+
},
29+
},
30+
{
31+
// Once we remove the legacy vows tests in ./test, we can remove these JS-specific rules
32+
files: ['test/**/*.js', 'eslint.config.mjs'],
33+
...tseslint.configs.disableTypeChecked,
34+
rules: {
35+
...tseslint.configs.disableTypeChecked.rules,
36+
'@typescript-eslint/explicit-function-return-type': 'off',
37+
'@typescript-eslint/no-var-requires': 'off',
38+
'@typescript-eslint/no-unused-vars': 'off',
39+
},
40+
},
41+
)

lib/__tests__/cookieJar.spec.ts

+14-10
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,9 @@ describe('CookieJar', () => {
909909
'should remove all the stored cookies',
910910
{
911911
callbackStyle(done) {
912-
cookieJar.removeAllCookies(() => done())
912+
cookieJar.removeAllCookies(() => {
913+
done()
914+
})
913915
},
914916
async asyncStyle() {
915917
await cookieJar.removeAllCookies()
@@ -1348,7 +1350,7 @@ describe.each(['local', 'example', 'invalid', 'localhost', 'test'])(
13481350
expect.objectContaining({
13491351
key: 'settingThisShouldPass',
13501352
value: 'true',
1351-
domain: `${specialUseDomain}`,
1353+
domain: specialUseDomain,
13521354
}),
13531355
)
13541356
const cookies = await cookieJar.getCookies(
@@ -1393,7 +1395,7 @@ describe.each(['local', 'example', 'invalid', 'localhost', 'test'])(
13931395
expect.objectContaining({
13941396
key: 'settingThisShouldPass',
13951397
value: 'true',
1396-
domain: `${specialUseDomain}`,
1398+
domain: specialUseDomain,
13971399
}),
13981400
)
13991401
const cookies = await cookieJar.getCookies(
@@ -1480,9 +1482,9 @@ describe('Synchronous API on async CookieJar', () => {
14801482

14811483
it('should throw an error when calling `removeAllCookiesSync` if store is not synchronous', () => {
14821484
const cookieJar = new CookieJar(store)
1483-
expect(() => cookieJar.removeAllCookiesSync()).toThrow(
1484-
'CookieJar store is not synchronous; use async API instead.',
1485-
)
1485+
expect(() => {
1486+
cookieJar.removeAllCookiesSync()
1487+
}).toThrow('CookieJar store is not synchronous; use async API instead.')
14861488
})
14871489
})
14881490

@@ -1496,7 +1498,7 @@ function createCookie(
14961498
if (!cookie) {
14971499
throw new Error('This should not be undefined')
14981500
}
1499-
if (options?.hostOnly) {
1501+
if (options.hostOnly) {
15001502
cookie.hostOnly = options.hostOnly
15011503
}
15021504
return cookie
@@ -1508,9 +1510,11 @@ function apiVariants(
15081510
assertions: () => void,
15091511
): void {
15101512
it(`${testName} (callback)`, async () => {
1511-
await new Promise((resolve) =>
1512-
apiVariants.callbackStyle(() => resolve(undefined)),
1513-
)
1513+
await new Promise((resolve) => {
1514+
apiVariants.callbackStyle(() => {
1515+
resolve(undefined)
1516+
})
1517+
})
15141518
assertions()
15151519
})
15161520

lib/__tests__/data/parser.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// This file just contains test data, so we don't care about the number of lines.
21
export default [
32
{
43
test: '0001',

lib/__tests__/jarSerialization.spec.ts

+4-13
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,9 @@ function expectDataToMatchSerializationSchema(
306306
expect(serializedJar.storeType).toBe('MemoryCookieStore')
307307
expect(serializedJar.rejectPublicSuffixes).toBe(true)
308308
expect(serializedJar.cookies).toBeInstanceOf(Array)
309-
serializedJar.cookies.forEach((cookie) => validateSerializedCookie(cookie))
309+
serializedJar.cookies.forEach((cookie) => {
310+
validateSerializedCookie(cookie)
311+
})
310312
}
311313

312314
const serializedCookiePropTypes: { [key: string]: string } = {
@@ -345,7 +347,7 @@ function validateSerializedCookie(cookie: SerializedCookie): void {
345347

346348
case 'intOrInf':
347349
if (cookie[prop] !== 'Infinity' && cookie[prop] !== '-Infinity') {
348-
expect(isInteger(cookie[prop])).toBe(true)
350+
expect(Number.isInteger(cookie[prop])).toBe(true)
349351
}
350352
break
351353

@@ -361,14 +363,3 @@ function validateSerializedCookie(cookie: SerializedCookie): void {
361363
}
362364
})
363365
}
364-
365-
function isInteger(value: unknown): boolean {
366-
if (Number.isInteger) {
367-
return Number.isInteger(value)
368-
}
369-
// Node 0.10 (still supported) doesn't have Number.isInteger
370-
// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
371-
return (
372-
typeof value === 'number' && isFinite(value) && Math.floor(value) === value
373-
)
374-
}

lib/__tests__/removeAll.spec.ts

+32-23
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@ describe('store removeAllCookies API', () => {
3333
// replace remove cookie behavior to throw an error on the 4th invocation
3434
const _removeCookie = store.removeCookie.bind(store)
3535
const spy = jest.spyOn(store, 'removeCookie')
36-
spy.mockImplementationOnce((domain, path, key, callback) =>
37-
_removeCookie.call(store, domain, path, key, callback),
38-
)
39-
spy.mockImplementationOnce((domain, path, key, callback) =>
40-
_removeCookie.call(store, domain, path, key, callback),
41-
)
42-
spy.mockImplementationOnce((domain, path, key, callback) =>
43-
_removeCookie.call(store, domain, path, key, callback),
44-
)
45-
spy.mockImplementationOnce((_domain, _path, _key, callback) =>
46-
callback(new Error('something happened 4')),
47-
)
36+
spy.mockImplementationOnce((domain, path, key, callback) => {
37+
_removeCookie.call(store, domain, path, key, callback)
38+
})
39+
spy.mockImplementationOnce((domain, path, key, callback) => {
40+
_removeCookie.call(store, domain, path, key, callback)
41+
})
42+
spy.mockImplementationOnce((domain, path, key, callback) => {
43+
_removeCookie.call(store, domain, path, key, callback)
44+
})
45+
spy.mockImplementationOnce((_domain, _path, _key, callback) => {
46+
callback(new Error('something happened 4'))
47+
})
4848

4949
await expect(jar.removeAllCookies()).rejects.toThrow(
5050
'something happened 4',
@@ -71,11 +71,12 @@ describe('store removeAllCookies API', () => {
7171
const spy = jest.spyOn(store, 'removeCookie')
7272
spy.mockImplementation((domain, path, key, callback) => {
7373
if (spy.mock.calls.length % 2 === 1) {
74-
return callback(
75-
new Error(`something happened ${spy.mock.calls.length}`),
74+
callback(
75+
new Error(`something happened ${String(spy.mock.calls.length)}`),
7676
)
77+
return
7778
}
78-
return _removeCookie.call(store, domain, path, key, callback)
79+
_removeCookie.call(store, domain, path, key, callback)
7980
})
8081

8182
await expect(jar.removeAllCookies()).rejects.toThrowError(
@@ -143,7 +144,8 @@ class StoreWithoutRemoveAll extends Store {
143144
if (!callback) {
144145
throw new Error('This should not be undefined')
145146
}
146-
return callback(null, undefined)
147+
callback(null, undefined)
148+
return
147149
}
148150

149151
override findCookies(
@@ -166,7 +168,8 @@ class StoreWithoutRemoveAll extends Store {
166168
if (!callback) {
167169
throw new Error('This should not be undefined')
168170
}
169-
return callback(null, [])
171+
callback(null, [])
172+
return
170173
}
171174

172175
override putCookie(cookie: Cookie): Promise<void>
@@ -177,7 +180,8 @@ class StoreWithoutRemoveAll extends Store {
177180
if (!callback) {
178181
throw new Error('This should not be undefined')
179182
}
180-
return callback(null)
183+
callback(null)
184+
return
181185
}
182186

183187
override getAllCookies(): Promise<Cookie[]>
@@ -187,7 +191,8 @@ class StoreWithoutRemoveAll extends Store {
187191
if (!callback) {
188192
throw new Error('This should not be undefined')
189193
}
190-
return callback(null, this.cookies.slice())
194+
callback(null, this.cookies.slice())
195+
return
191196
}
192197

193198
override removeCookie(
@@ -211,7 +216,8 @@ class StoreWithoutRemoveAll extends Store {
211216
if (!callback) {
212217
throw new Error('This should not be undefined')
213218
}
214-
return callback(null)
219+
callback(null)
220+
return
215221
}
216222
}
217223

@@ -234,7 +240,8 @@ class MemoryStoreExtension extends MemoryCookieStore {
234240
if (!callback) {
235241
throw new Error('This should not be undefined')
236242
}
237-
return super.getAllCookies(callback)
243+
super.getAllCookies(callback)
244+
return
238245
}
239246

240247
override removeCookie(
@@ -258,7 +265,8 @@ class MemoryStoreExtension extends MemoryCookieStore {
258265
if (!callback) {
259266
throw new Error('This should not be undefined')
260267
}
261-
return super.removeCookie(domain, path, key, callback)
268+
super.removeCookie(domain, path, key, callback)
269+
return
262270
}
263271

264272
override removeAllCookies(): Promise<void>
@@ -268,6 +276,7 @@ class MemoryStoreExtension extends MemoryCookieStore {
268276
if (!callback) {
269277
throw new Error('This should not be undefined')
270278
}
271-
return super.removeAllCookies(callback)
279+
super.removeAllCookies(callback)
280+
return
272281
}
273282
}

lib/cookie/cookie.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ function parse(str: string, options?: ParseCookieOptions): Cookie | undefined {
164164
continue
165165
}
166166
const av_sep = av.indexOf('=')
167-
let av_key, av_value
167+
let av_key: string, av_value: string | null
168168

169169
if (av_sep === -1) {
170170
av_key = av
@@ -551,11 +551,11 @@ export class Cookie {
551551
const hostOnly = this.hostOnly != null ? this.hostOnly.toString() : '?'
552552
const createAge =
553553
this.creation && this.creation !== 'Infinity'
554-
? `${now - this.creation.getTime()}ms`
554+
? `${String(now - this.creation.getTime())}ms`
555555
: '?'
556556
const accessAge =
557557
this.lastAccessed && this.lastAccessed !== 'Infinity'
558-
? `${now - this.lastAccessed.getTime()}ms`
558+
? `${String(now - this.lastAccessed.getTime())}ms`
559559
: '?'
560560
return `Cookie="${this.toString()}; hostOnly=${hostOnly}; aAge=${accessAge}; cAge=${createAge}"`
561561
}
@@ -657,7 +657,7 @@ export class Cookie {
657657
* @beta
658658
*/
659659
validate(): boolean {
660-
if (this.value == null || !COOKIE_OCTETS.test(this.value)) {
660+
if (!this.value || !COOKIE_OCTETS.test(this.value)) {
661661
return false
662662
}
663663
if (
@@ -732,7 +732,7 @@ export class Cookie {
732732
* @public
733733
*/
734734
cookieString(): string {
735-
const val = this.value ?? ''
735+
const val = this.value || ''
736736
if (this.key) {
737737
return `${this.key}=${val}`
738738
}
@@ -753,7 +753,7 @@ export class Cookie {
753753
}
754754

755755
if (this.maxAge != null && this.maxAge != Infinity) {
756-
str += `; Max-Age=${this.maxAge}`
756+
str += `; Max-Age=${String(this.maxAge)}`
757757
}
758758

759759
if (this.domain && !this.hostOnly) {

lib/cookie/cookieCompare.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export function cookieCompare(a: Cookie, b: Cookie): number {
8888
}
8989

9090
// break ties for the same millisecond (precision of JavaScript's clock)
91-
cmp = (a.creationIndex ?? 0) - (b.creationIndex ?? 0)
91+
cmp = (a.creationIndex || 0) - (b.creationIndex || 0)
9292

9393
return cmp
9494
}

0 commit comments

Comments
 (0)