Skip to content

Commit

Permalink
requiredWhen followup
Browse files Browse the repository at this point in the history
  • Loading branch information
af committed Sep 23, 2024
1 parent 1d9d237 commit 6e6f789
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 32 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Each validation function accepts an (optional) object with the following attribu
- `desc` - A string that describes the env var.
- `example` - An example value for the env var.
- `docs` - A URL that leads to more detailed documentation about the env var.
- `requiredWhen` - A boolean function that specify when the env var is required. Use With default: undefined (optional value).
- `requiredWhen` - A function (env -> boolean) specifying when this env var is required. Use With default: undefined (optional value).

## Custom validators

Expand Down
4 changes: 3 additions & 1 deletion src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>) {
export function formatSpecDescription<T>(spec: Spec<T>) {
const egText = spec.example ? ` (eg. "${spec.example}")` : ''
const docsText = spec.docs ? `. See ${spec.docs}` : ''
return `${spec.desc}${egText}${docsText}`
Expand Down Expand Up @@ -100,6 +100,8 @@ export function getSanitizedEnv<S>(
}
}

// This block is for supporting requiredWhen. If that field was provided for a var's spec and
// its condition evaluates to a truthy value, ensure that env var is present.
for (const k of varKeys) {
if (errors[k] == undefined) {
const spec = castedSpecs[k]
Expand Down
26 changes: 15 additions & 11 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ export interface Spec<T> {
*/
devDefault?: NonNullable<T> | undefined

requiredWhen?: (cleanedEnv: any) => boolean | undefined
/**
* A function (env -> boolean) that allows an env var to be required only when certain
* conditions are met with the rest of the env object. Use With default: undefined.
*/
requiredWhen?: (cleanedEnv: Record<string, unknown>) => boolean | undefined
}

type OptionalAttrs<T> =
Expand Down Expand Up @@ -103,16 +107,16 @@ export type SpecsOutput<S> = {

export type CleanedEnv<S> =
S extends Record<string, ValidatorSpec<unknown>>
? Readonly<
{
[K in keyof S]: S[K] extends OptionalValidatorSpec<infer U>
? U | undefined
: S[K] extends RequiredValidatorSpec<infer U>
? U
: never
} & CleanedEnvAccessors
>
: never
? Readonly<
{
[K in keyof S]: S[K] extends OptionalValidatorSpec<infer U>
? U | undefined
: S[K] extends RequiredValidatorSpec<infer U>
? U
: never
} & CleanedEnvAccessors
>
: never

export interface CleanedEnvAccessors {
/** true if NODE_ENV === 'development' */
Expand Down
30 changes: 11 additions & 19 deletions tests/requiredWhen.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
// let defaultReporter: jest.Mock = jest.fn().mockImplementation(() => {})
// jest.mock('../src/reporter.ts', () => {
// return {
// defaultReporter: defaultReporter,
// }
// })
import { bool, cleanEnv, defaultReporter, EnvMissingError, Spec, num, EnvError } from '../src'
import { bool, cleanEnv, defaultReporter, EnvMissingError, num, EnvError } from '../src'
import { formatSpecDescription } from '../src/core'

jest.mock('../src/reporter')
const mockedDefaultReporter: jest.Mock = <jest.Mock<typeof defaultReporter>> defaultReporter;
mockedDefaultReporter.mockImplementation((a) => {console.log(a)})
describe('required when', () => {
const mockedDefaultReporter: jest.Mock = <jest.Mock<typeof defaultReporter>>defaultReporter;
mockedDefaultReporter.mockImplementation(() => { })

describe('requiredWhen', () => {
beforeEach(() => {
mockedDefaultReporter.mockClear()
})
Expand All @@ -35,7 +32,7 @@ describe('required when', () => {
})
})

test("required but not provided", () => {
test('required but not provided', () => {
cleanEnv(
{
autoExtractId: "false",
Expand Down Expand Up @@ -67,7 +64,7 @@ describe('required when', () => {
})
})

test("required and provided", () => {
test('required and provided', () => {
cleanEnv(
{
autoExtractId: "false",
Expand All @@ -90,8 +87,8 @@ describe('required when', () => {
errors: {},
})
})
test("required but failed to parse", () => {

test('required but failed to parse', () => {
cleanEnv(
{
autoExtractId: "false",
Expand All @@ -117,8 +114,3 @@ describe('required when', () => {
})
})
})
function formatSpecDescription<T>(spec: Spec<T>) {
const egText = spec.example ? ` (eg. "${spec.example}")` : ''
const docsText = spec.docs ? `. See ${spec.docs}` : ''
return `${spec.desc}${egText}${docsText}`
}

0 comments on commit 6e6f789

Please sign in to comment.