From 3790afbc5f13e5675fb344a8baaff75cd64dfe82 Mon Sep 17 00:00:00 2001 From: Zirak Date: Wed, 19 Jan 2022 15:35:59 +0000 Subject: [PATCH] feat(load): accept functions as parser presets Previously, `load` could accept `parserPresets` as both values and promises of values. Per #2964 however, there was expectations for it to also support functions returning said values. This commit enables to specify `parserPresets` in all the same ways as rules. That is, both commitlint rules and `parserPresets` can be specified as: - Plain values - Promises - Functions returning plain values - Functions returning promises Solves #2964. --- .../src/__snapshots__/validate.test.ts.snap | 1 + .../src/commitlint.schema.json | 3 +- .../load/src/utils/load-parser-opts.test.ts | 55 +++++++++++++++++++ .../load/src/utils/load-parser-opts.ts | 12 +++- 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 @commitlint/load/src/utils/load-parser-opts.test.ts diff --git a/@commitlint/config-validator/src/__snapshots__/validate.test.ts.snap b/@commitlint/config-validator/src/__snapshots__/validate.test.ts.snap index bb626b6ee2..a4cb4a8c86 100644 --- a/@commitlint/config-validator/src/__snapshots__/validate.test.ts.snap +++ b/@commitlint/config-validator/src/__snapshots__/validate.test.ts.snap @@ -56,6 +56,7 @@ exports[`validation should fail for parserPreset 1`] = ` "Commitlint configuration in parserPreset.js is invalid: - Property \\"parserPreset\\" has the wrong type - should be string. - Property \\"parserPreset\\" has the wrong type - should be object. + - \\"parserPreset\\" should be a function. Value: []. - \\"parserPreset\\" should match exactly one schema in oneOf. Value: []. " `; diff --git a/@commitlint/config-validator/src/commitlint.schema.json b/@commitlint/config-validator/src/commitlint.schema.json index 62e70c356e..33870445ba 100644 --- a/@commitlint/config-validator/src/commitlint.schema.json +++ b/@commitlint/config-validator/src/commitlint.schema.json @@ -57,7 +57,8 @@ "parserOpts": {} }, "additionalProperties": true - } + }, + {"typeof": "function"} ] }, "helpUrl": { diff --git a/@commitlint/load/src/utils/load-parser-opts.test.ts b/@commitlint/load/src/utils/load-parser-opts.test.ts new file mode 100644 index 0000000000..f096043116 --- /dev/null +++ b/@commitlint/load/src/utils/load-parser-opts.test.ts @@ -0,0 +1,55 @@ +import {loadParserOpts} from './load-parser-opts'; + +test('handles a plain preset', async () => { + const preset = { + parserOpts: {}, + }; + + expect(await loadParserOpts(preset)).toEqual(preset); +}); + +test('handles primitive values', async () => { + expect(await loadParserOpts('')).toEqual(undefined); + expect(await loadParserOpts(undefined)).toEqual(undefined); +}); + +test('handles an object without any parserOpts', async () => { + const preset = {}; + expect(await loadParserOpts(preset)).toEqual(preset); +}); + +test('handles nested parserOpts', async () => { + const opts = {a: 4}; + + // plain nested parserOpts + let loaded = await loadParserOpts({ + parserOpts: { + parserOpts: opts, + }, + }); + expect(loaded).toHaveProperty('parserOpts', opts); + + // async nested parserOpts + loaded = await loadParserOpts({ + parserOpts: Promise.resolve({ + parserOpts: opts, + }), + }); + expect(loaded).toHaveProperty('parserOpts', opts); +}); + +test('runs a sync function which returns the preset', async () => { + const preset = {}; + const fn = () => preset; + const opts = await loadParserOpts(fn); + + expect(opts).toEqual(preset); +}); + +test('runs an async function which returns the preset', async () => { + const preset = {}; + const fn = async () => preset; + const opts = await loadParserOpts(fn); + + expect(opts).toEqual(preset); +}); diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index fe24ecb549..11bcdcb119 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -1,5 +1,7 @@ import {ParserPreset} from '@commitlint/types'; +type Awaitable = T | PromiseLike; + function isObjectLike(obj: unknown): obj is Record { return Boolean(obj) && typeof obj === 'object'; // typeof null === 'object' } @@ -15,8 +17,16 @@ function isParserOptsFunction( } export async function loadParserOpts( - pendingParser: string | ParserPreset | Promise | undefined + pendingParser: + | string + | Awaitable + | (() => Awaitable) + | undefined ): Promise { + if (typeof pendingParser === 'function') { + return loadParserOpts(pendingParser()); + } + if (!pendingParser || typeof pendingParser !== 'object') { return undefined; }