Skip to content

Commit

Permalink
Fix af#155 and upgrade jest + node version
Browse files Browse the repository at this point in the history
  • Loading branch information
Antoine Pous committed Oct 18, 2021
1 parent 4da4f18 commit 5a9bcf4
Show file tree
Hide file tree
Showing 8 changed files with 404 additions and 378 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [ 10, 12, 14 ]
node-version: [ 10, 12, 14, 16 ]

steps:
- uses: actions/checkout@v2
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"prepare": "tsc",
"coverage": "jest --coverage",
"lint": "tsc && prettier -list-different --write src tests",
"test": "jest"
"test": "jest -i"
},
"repository": {
"type": "git",
Expand All @@ -40,9 +40,9 @@
"@types/jest": "26.0.24",
"@types/node": "16.4.0",
"husky": "4.3.6",
"jest": "27.0.6",
"jest": "27.3.0",
"prettier": "2.3.2",
"ts-jest": "27.0.4",
"ts-jest": "27.0.7",
"typescript": "4.3.5"
},
"author": "Aaron Franks",
Expand Down
4 changes: 2 additions & 2 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function validateVar<T>({
}: {
name: string
rawValue: string | T
spec: Spec<T> & { _parse: (input: string) => T }
spec: Spec & { _parse: (input: string) => T }
}) {
if (typeof spec._parse !== 'function') {
throw new EnvError(`Invalid spec for "${name}"`)
Expand All @@ -36,7 +36,7 @@ function validateVar<T>({
}

// Format a string error message for when a required env var is missing
function formatSpecDescription<T>(spec: Spec<T>) {
function formatSpecDescription(spec: Spec) {
const egText = spec.example ? ` (eg. "${spec.example}")` : ''
const docsText = spec.docs ? `. See ${spec.docs}` : ''
return `${spec.desc}${egText}${docsText}`
Expand Down
10 changes: 5 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
export interface Spec<T> {
export interface Spec {
/**
* An Array that lists the admissable parsed values for the env var.
*/
choices?: ReadonlyArray<T>
choices?: ReadonlyArray<string>
/**
* A fallback value, which will be used if the env var wasn't specified. Providing a default effectively makes the env var optional.
*/
default?: T
default?: string
/**
* A fallback value to use only when NODE_ENV is not 'production'.
* This is handy for env vars that are required for production environments, but optional for development and testing.
*/
devDefault?: T
devDefault?: string
/**
* A string that describes the env var.
*/
Expand All @@ -26,7 +26,7 @@ export interface Spec<T> {
docs?: string
}

export interface ValidatorSpec<T> extends Spec<T> {
export interface ValidatorSpec<T> extends Spec {
_parse: (input: string) => T
}

Expand Down
51 changes: 24 additions & 27 deletions src/validators.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Spec, ValidatorSpec } from './types'
import { EnvError } from './errors'
import { URL } from 'url'

// Simplified adaptation of https://github.com/validatorjs/validator.js/blob/master/src/lib/isFQDN.js
const isFQDN = (input: string) => {
Expand Down Expand Up @@ -28,23 +29,21 @@ const isIP = (input: string) => {
const EMAIL_REGEX = /^[^@\s]+@[^@\s]+\.[^@\s]+$/ // intentionally non-exhaustive

export const makeValidator = <T>(parseFn: (input: string) => T) => {
return function (spec?: Spec<T>): ValidatorSpec<T> {
return function (spec?: Spec): ValidatorSpec<T> {
return { ...spec, _parse: parseFn }
}
}

// The reason for the function wrapper is to enable the <T extends boolean = boolean> type parameter
// that enables better type inference. For more context, check out the following PR:
// https://github.com/af/envalid/pull/118
export function bool<T extends boolean = boolean>(spec?: Spec<T>) {
return makeValidator((input: string | boolean) => {
export function bool(spec?: Spec) {
return makeValidator<boolean>((input: string): boolean => {
switch (input) {
case true:
case 'true':
case 't':
case '1':
return true
case false:
case 'false':
case 'f':
case '0':
Expand All @@ -55,39 +54,39 @@ export function bool<T extends boolean = boolean>(spec?: Spec<T>) {
})(spec)
}

export function num<T extends number = number>(spec?: Spec<T>) {
return makeValidator((input: string) => {
export function num(spec?: Spec) {
return makeValidator<number>((input: string): number => {
const coerced = parseFloat(input)
if (Number.isNaN(coerced)) throw new EnvError(`Invalid number input: "${input}"`)
return coerced
})(spec)
}

export function str<T extends string = string>(spec?: Spec<T>) {
return makeValidator((input: string) => {
if (typeof input === 'string') return input
export function str(spec?: Spec) {
return makeValidator<string>((input: string): string => {
if (input.trim() !== '') return input
throw new EnvError(`Not a string: "${input}"`)
})(spec)
}

export function email<T extends string = string>(spec?: Spec<T>) {
return makeValidator((x: string) => {
if (EMAIL_REGEX.test(x)) return x
throw new EnvError(`Invalid email address: "${x}"`)
export function email(spec?: Spec) {
return makeValidator<string>((input: string): string => {
if (EMAIL_REGEX.test(input)) return input
throw new EnvError(`Invalid email address: "${input}"`)
})(spec)
}

export function host<T extends string = string>(spec?: Spec<T>) {
return makeValidator((input: string) => {
export function host(spec?: Spec) {
return makeValidator<string>((input: string): string => {
if (!isFQDN(input) && !isIP(input)) {
throw new EnvError(`Invalid host (domain or ip): "${input}"`)
}
return input
})(spec)
}

export function port<T extends number = number>(spec?: Spec<T>) {
return makeValidator((input: string) => {
export function port(spec?: Spec) {
return makeValidator<number>((input: string): number => {
const coerced = +input
if (
Number.isNaN(coerced) ||
Expand All @@ -102,12 +101,10 @@ export function port<T extends number = number>(spec?: Spec<T>) {
})(spec)
}

export function url<T extends string = string>(spec?: Spec<T>) {
return makeValidator((x: string) => {
export function url(spec?: Spec) {
return makeValidator<URL>((x: string): URL => {
try {
// @ts-expect-error TS doesn't acknowledge this API by default yet
new URL(x)
return x
return new URL(x)
} catch (e) {
throw new EnvError(`Invalid url: "${x}"`)
}
Expand All @@ -120,12 +117,12 @@ export function url<T extends string = string>(spec?: Spec<T>) {
// cleanEnv({
// MY_VAR: json<{ foo: number }>({ default: { foo: 123 } }),
// })
export function json<T = any>(spec?: Spec<T>) {
return makeValidator<T>((x: string) => {
export function json(spec?: Spec) {
return makeValidator<unknown>((input: string): unknown => {
try {
return JSON.parse(x)
return JSON.parse(input)
} catch (e) {
throw new EnvError(`Invalid json: "${x}"`)
throw new EnvError(`Invalid json: "${input}"`)
}
})(spec)
}
2 changes: 1 addition & 1 deletion tests/basics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ test('falsy devDefault', () => {

test('devDefault and default together', () => {
const spec = {
FOO: num({ devDefault: 3000, default: 80 }),
FOO: num({ devDefault: '3000', default: '80' }),
}

const env = cleanEnv({ NODE_ENV: 'test' }, spec)
Expand Down
2 changes: 1 addition & 1 deletion tests/validators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test('bool() works with various formats', () => {
const f = cleanEnv({ FOO: 'f' }, { FOO: bool() })
expect(f).toEqual({ FOO: false })

const defaultF = cleanEnv({}, { FOO: bool({ default: false }) })
const defaultF = cleanEnv({}, { FOO: bool({ default: 'false' }) })
expect(defaultF).toEqual({ FOO: false })
})

Expand Down
Loading

0 comments on commit 5a9bcf4

Please sign in to comment.