diff --git a/README.md b/README.md index b42bc40..5a48c4b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/core.ts b/src/core.ts index ea6fbb5..38c6a62 100644 --- a/src/core.ts +++ b/src/core.ts @@ -36,7 +36,7 @@ function validateVar({ } // Format a string error message for when a required env var is missing -function formatSpecDescription(spec: Spec) { +export function formatSpecDescription(spec: Spec) { const egText = spec.example ? ` (eg. "${spec.example}")` : '' const docsText = spec.docs ? `. See ${spec.docs}` : '' return `${spec.desc}${egText}${docsText}` @@ -100,6 +100,8 @@ export function getSanitizedEnv( } } + // 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] diff --git a/src/types.ts b/src/types.ts index cc0f772..50e62b6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,7 +25,11 @@ export interface Spec { */ devDefault?: NonNullable | 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) => boolean | undefined } type OptionalAttrs = @@ -103,16 +107,16 @@ export type SpecsOutput = { export type CleanedEnv = S extends Record> - ? Readonly< - { - [K in keyof S]: S[K] extends OptionalValidatorSpec - ? U | undefined - : S[K] extends RequiredValidatorSpec - ? U - : never - } & CleanedEnvAccessors - > - : never + ? Readonly< + { + [K in keyof S]: S[K] extends OptionalValidatorSpec + ? U | undefined + : S[K] extends RequiredValidatorSpec + ? U + : never + } & CleanedEnvAccessors + > + : never export interface CleanedEnvAccessors { /** true if NODE_ENV === 'development' */ diff --git a/tests/requiredWhen.test.ts b/tests/requiredWhen.test.ts index 0142b1b..be9a11a 100644 --- a/tests/requiredWhen.test.ts +++ b/tests/requiredWhen.test.ts @@ -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 = > defaultReporter; -mockedDefaultReporter.mockImplementation((a) => {console.log(a)}) -describe('required when', () => { +const mockedDefaultReporter: jest.Mock = >defaultReporter; +mockedDefaultReporter.mockImplementation(() => { }) + +describe('requiredWhen', () => { beforeEach(() => { mockedDefaultReporter.mockClear() }) @@ -35,7 +32,7 @@ describe('required when', () => { }) }) - test("required but not provided", () => { + test('required but not provided', () => { cleanEnv( { autoExtractId: "false", @@ -67,7 +64,7 @@ describe('required when', () => { }) }) - test("required and provided", () => { + test('required and provided', () => { cleanEnv( { autoExtractId: "false", @@ -90,8 +87,8 @@ describe('required when', () => { errors: {}, }) }) - - test("required but failed to parse", () => { + + test('required but failed to parse', () => { cleanEnv( { autoExtractId: "false", @@ -117,8 +114,3 @@ describe('required when', () => { }) }) }) -function formatSpecDescription(spec: Spec) { - const egText = spec.example ? ` (eg. "${spec.example}")` : '' - const docsText = spec.docs ? `. See ${spec.docs}` : '' - return `${spec.desc}${egText}${docsText}` -}