From d315e02e0f66c20f94a597dbac33ef23e61059fc Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 10 Jan 2021 21:18:53 +0100 Subject: [PATCH 01/24] feat: basic user config validation --- @commitlint/cli/src/cli.ts | 2 +- @commitlint/load/src/load.test.ts | 44 +++--- @commitlint/load/src/load.ts | 129 ++++++++---------- .../load/src/utils/load-parser-opts.ts | 59 ++++---- @commitlint/load/src/utils/validators.ts | 59 ++++++++ @commitlint/resolve-extends/package.json | 1 + @commitlint/resolve-extends/src/index.ts | 11 +- @commitlint/resolve-extends/tsconfig.json | 3 +- @commitlint/types/src/load.ts | 10 +- 9 files changed, 192 insertions(+), 126 deletions(-) create mode 100644 @commitlint/load/src/utils/validators.ts diff --git a/@commitlint/cli/src/cli.ts b/@commitlint/cli/src/cli.ts index 0e0603ba67..688cb0f1e4 100644 --- a/@commitlint/cli/src/cli.ts +++ b/@commitlint/cli/src/cli.ts @@ -341,7 +341,7 @@ function getSeed(flags: CliFlags): Seed { : {parserPreset: flags['parser-preset']}; } -function selectParserOpts(parserPreset: ParserPreset) { +function selectParserOpts(parserPreset: ParserPreset | undefined) { if (typeof parserPreset !== 'object') { return undefined; } diff --git a/@commitlint/load/src/load.test.ts b/@commitlint/load/src/load.test.ts index d192ff3548..75d0fc572a 100644 --- a/@commitlint/load/src/load.test.ts +++ b/@commitlint/load/src/load.test.ts @@ -21,6 +21,7 @@ test('extends-empty should have no rules', async () => { const actual = await load({}, {cwd}); expect(actual.rules).toMatchObject({}); + expect(actual.parserPreset).not.toBeDefined(); }); test('uses seed as configured', async () => { @@ -127,8 +128,9 @@ test('uses seed with parserPreset', async () => { {cwd} ); - expect(actual.name).toBe('./conventional-changelog-custom'); - expect(actual.parserOpts).toMatchObject({ + expect(actual).toBeDefined(); + expect(actual!.name).toBe('./conventional-changelog-custom'); + expect(actual!.parserOpts).toMatchObject({ headerPattern: /^(\w*)(?:\((.*)\))?-(.*)$/, }); }); @@ -252,8 +254,9 @@ test('parser preset overwrites completely instead of merging', async () => { const cwd = await gitBootstrap('fixtures/parser-preset-override'); const actual = await load({}, {cwd}); - expect(actual.parserPreset.name).toBe('./custom'); - expect(actual.parserPreset.parserOpts).toMatchObject({ + expect(actual.parserPreset).toBeDefined(); + expect(actual.parserPreset!.name).toBe('./custom'); + expect(actual.parserPreset!.parserOpts).toMatchObject({ headerPattern: /.*/, }); }); @@ -262,8 +265,9 @@ test('recursive extends with parserPreset', async () => { const cwd = await gitBootstrap('fixtures/recursive-parser-preset'); const actual = await load({}, {cwd}); - expect(actual.parserPreset.name).toBe('./conventional-changelog-custom'); - expect(actual.parserPreset.parserOpts).toMatchObject({ + expect(actual.parserPreset).toBeDefined(); + expect(actual.parserPreset!.name).toBe('./conventional-changelog-custom'); + expect(actual.parserPreset!.parserOpts).toMatchObject({ headerPattern: /^(\w*)(?:\((.*)\))?-(.*)$/, }); }); @@ -386,11 +390,12 @@ test('resolves parser preset from conventional commits', async () => { const cwd = await npmBootstrap('fixtures/parser-preset-conventionalcommits'); const actual = await load({}, {cwd}); - expect(actual.parserPreset.name).toBe( + expect(actual.parserPreset).toBeDefined(); + expect(actual.parserPreset!.name).toBe( 'conventional-changelog-conventionalcommits' ); - expect(typeof actual.parserPreset.parserOpts).toBe('object'); - expect((actual.parserPreset.parserOpts as any).headerPattern).toEqual( + expect(typeof actual.parserPreset!.parserOpts).toBe('object'); + expect((actual.parserPreset!.parserOpts as any).headerPattern).toEqual( /^(\w*)(?:\((.*)\))?!?: (.*)$/ ); }); @@ -399,9 +404,10 @@ test('resolves parser preset from conventional angular', async () => { const cwd = await npmBootstrap('fixtures/parser-preset-angular'); const actual = await load({}, {cwd}); - expect(actual.parserPreset.name).toBe('conventional-changelog-angular'); - expect(typeof actual.parserPreset.parserOpts).toBe('object'); - expect((actual.parserPreset.parserOpts as any).headerPattern).toEqual( + expect(actual.parserPreset).toBeDefined(); + expect(actual.parserPreset!.name).toBe('conventional-changelog-angular'); + expect(typeof actual.parserPreset!.parserOpts).toBe('object'); + expect((actual.parserPreset!.parserOpts as any).headerPattern).toEqual( /^(\w*)(?:\((.*)\))?: (.*)$/ ); }); @@ -416,9 +422,10 @@ test('recursive resolves parser preset from conventional atom', async () => { const actual = await load({}, {cwd}); - expect(actual.parserPreset.name).toBe('conventional-changelog-atom'); - expect(typeof actual.parserPreset.parserOpts).toBe('object'); - expect((actual.parserPreset.parserOpts as any).headerPattern).toEqual( + expect(actual.parserPreset).toBeDefined(); + expect(actual.parserPreset!.name).toBe('conventional-changelog-atom'); + expect(typeof actual.parserPreset!.parserOpts).toBe('object'); + expect((actual.parserPreset!.parserOpts as any).headerPattern).toEqual( /^(:.*?:) (.*)$/ ); }); @@ -429,11 +436,12 @@ test('resolves parser preset from conventional commits without factory support', ); const actual = await load({}, {cwd}); - expect(actual.parserPreset.name).toBe( + expect(actual.parserPreset).toBeDefined(); + expect(actual.parserPreset!.name).toBe( 'conventional-changelog-conventionalcommits' ); - expect(typeof actual.parserPreset.parserOpts).toBe('object'); - expect((actual.parserPreset.parserOpts as any).headerPattern).toEqual( + expect(typeof actual.parserPreset!.parserOpts).toBe('object'); + expect((actual.parserPreset!.parserOpts as any).headerPattern).toEqual( /^(\w*)(?:\((.*)\))?!?: (.*)$/ ); }); diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index f6b2e7d002..d03c18d0bd 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -1,8 +1,6 @@ import Path from 'path'; import merge from 'lodash/merge'; -import mergeWith from 'lodash/mergeWith'; -import pick from 'lodash/pick'; import union from 'lodash/union'; import resolveFrom from 'resolve-from'; @@ -12,18 +10,15 @@ import { UserConfig, LoadOptions, QualifiedConfig, - UserPreset, QualifiedRules, - ParserPreset, + PluginRecords, } from '@commitlint/types'; import loadPlugin from './utils/load-plugin'; import {loadConfig} from './utils/load-config'; -import {loadParserOpts} from './utils/load-parser-opts'; +import {loadParser} from './utils/load-parser-opts'; import {pickConfig} from './utils/pick-config'; - -const w = (_: unknown, b: ArrayLike | null | undefined | false) => - Array.isArray(b) ? b : undefined; +import {validateConfig} from './utils/validators'; export default async function load( seed: UserConfig = {}, @@ -37,11 +32,17 @@ export default async function load( // Might amount to breaking changes, defer until 9.0.0 // Merge passed config with file based options - const config = pickConfig(merge({}, loaded ? loaded.config : null, seed)); - - const opts = merge( - {extends: [], rules: {}, formatter: '@commitlint/format'}, - pick(config, 'extends', 'plugins', 'ignores', 'defaultIgnores') + const config = pickConfig( + merge( + { + rules: {}, + formatter: '@commitlint/format', + helpUrl: + 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint', + }, + loaded ? loaded.config : null, + seed + ) ); // Resolve parserPreset key @@ -56,75 +57,63 @@ export default async function load( } // Resolve extends key - const extended = resolveExtends(opts, { + const extended = resolveExtends(config, { prefix: 'commitlint-config', cwd: base, parserPreset: config.parserPreset, }); - const preset = (pickConfig( - mergeWith(extended, config, w) - ) as unknown) as UserPreset; - preset.plugins = {}; - - // TODO: check if this is still necessary with the new factory based conventional changelog parsers - // config.extends = Array.isArray(config.extends) ? config.extends : []; - - // Resolve parser-opts from preset - if (typeof preset.parserPreset === 'object') { - preset.parserPreset.parserOpts = await loadParserOpts( - preset.parserPreset.name, - // TODO: fix the types for factory based conventional changelog parsers - preset.parserPreset as any - ); - } - - // Resolve config-relative formatter module - if (typeof config.formatter === 'string') { - preset.formatter = - resolveFrom.silent(base, config.formatter) || config.formatter; - } - - // Read plugins from extends - if (Array.isArray(extended.plugins)) { - config.plugins = union(config.plugins, extended.plugins || []); - } - - // resolve plugins - if (Array.isArray(config.plugins)) { - config.plugins.forEach((plugin) => { - if (typeof plugin === 'string') { - loadPlugin(preset.plugins, plugin, process.env.DEBUG === 'true'); - } else { - preset.plugins.local = plugin; - } - }); - } + validateConfig(extended); + + let plugins: PluginRecords = {}; + // TODO: this object merging should be done in resolveExtends + union( + // Read plugins from config + Array.isArray(config.plugins) ? config.plugins : [], + // Read plugins from extends + Array.isArray(extended.plugins) ? extended.plugins : [] + ).forEach((plugin) => { + if (typeof plugin === 'string') { + plugins = loadPlugin(plugins, plugin, process.env.DEBUG === 'true'); + } else { + plugins.local = plugin; + } + }); - const rules = preset.rules ? preset.rules : {}; - const qualifiedRules = ( + const rules = ( await Promise.all( - Object.entries(rules || {}).map((entry) => executeRule(entry)) + Object.entries({ + ...(typeof extended.rules === 'object' ? extended.rules || {} : {}), + ...(typeof config.rules === 'object' ? config.rules || {} : {}), + }).map((entry) => executeRule(entry)) ) ).reduce((registry, item) => { - const [key, value] = item as any; - (registry as any)[key] = value; + // type of `item` can be null, but Object.entries always returns key pair + const [key, value] = item!; + registry[key] = value; return registry; }, {}); - const helpUrl = - typeof config.helpUrl === 'string' - ? config.helpUrl - : 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'; - return { - extends: preset.extends!, - formatter: preset.formatter!, - parserPreset: preset.parserPreset! as ParserPreset, - ignores: preset.ignores!, - defaultIgnores: preset.defaultIgnores!, - plugins: preset.plugins!, - rules: qualifiedRules, - helpUrl, + // TODO: check if this is still necessary with the new factory based conventional changelog parsers + // TODO: should this function return this? as those values are already resolved + extends: Array.isArray(extended.extends) + ? extended.extends + : typeof extended.extends === 'string' + ? [extended.extends] + : [], + // Resolve config-relative formatter module + formatter: + resolveFrom.silent(base, extended.formatter) || extended.formatter, + // Resolve parser-opts from preset + parserPreset: await loadParser(extended.parserPreset), + ignores: extended.ignores, + defaultIgnores: extended.defaultIgnores, + plugins: plugins, + rules: rules, + helpUrl: + typeof extended.helpUrl === 'string' + ? extended.helpUrl + : 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint', }; } diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index 86d5b5b700..5380f529ba 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -1,48 +1,61 @@ -export async function loadParserOpts( - parserName: string, - pendingParser: Promise -) { +import {ParserPreset} from '@commitlint/types'; +import { + isObjectLike, + isParserOptsFunction, + isPromiseLike, + validateParser, +} from './validators'; + +export async function loadParser( + pendingParser: unknown +): Promise { + if (!pendingParser) { + return undefined; + } // Await for the module, loaded with require const parser = await pendingParser; + validateParser(parser); + // Await parser opts if applicable - if ( - typeof parser === 'object' && - typeof parser.parserOpts === 'object' && - typeof parser.parserOpts.then === 'function' - ) { - return (await parser.parserOpts).parserOpts; + if (isPromiseLike(parser.parserOpts)) { + parser.parserOpts = ((await parser.parserOpts) as any).parserOpts; + return parser; } // Create parser opts from factory if ( - typeof parser === 'object' && - typeof parser.parserOpts === 'function' && - parserName.startsWith('conventional-changelog-') + isParserOptsFunction(parser) && + parser.name.startsWith('conventional-changelog-') ) { - return await new Promise((resolve) => { - const result = parser.parserOpts((_: never, opts: {parserOpts: any}) => { - resolve(opts.parserOpts); + return new Promise((resolve) => { + const result = parser.parserOpts((_: never, opts) => { + resolve({ + ...parser, + parserOpts: opts.parserOpts, + }); }); // If result has data or a promise, the parser doesn't support factory-init // due to https://github.com/nodejs/promises-debugging/issues/16 it just quits, so let's use this fallback if (result) { Promise.resolve(result).then((opts) => { - resolve(opts.parserOpts); + resolve({ + ...parser, + parserOpts: opts.parserOpts, + }); }); } + return; }); } - // Pull nested paserOpts, might happen if overwritten with a module in main config + // Pull nested parserOpts, might happen if overwritten with a module in main config if ( - typeof parser === 'object' && - typeof parser.parserOpts === 'object' && + isObjectLike(parser.parserOpts) && typeof parser.parserOpts.parserOpts === 'object' ) { - return parser.parserOpts.parserOpts; + parser.parserOpts = parser.parserOpts.parserOpts; } - - return parser.parserOpts; + return parser; } diff --git a/@commitlint/load/src/utils/validators.ts b/@commitlint/load/src/utils/validators.ts new file mode 100644 index 0000000000..f26da6aaf5 --- /dev/null +++ b/@commitlint/load/src/utils/validators.ts @@ -0,0 +1,59 @@ +export function isObjectLike(obj: unknown): obj is Record { + return Boolean(obj) && typeof obj === 'object'; // typeof null === 'object' +} + +export function isPromiseLike(obj: unknown): obj is Promise { + return ( + (typeof obj === 'object' || typeof obj === 'function') && + typeof (obj as any).then === 'function' + ); +} + +export function isParserOptsFunction>( + obj: T +): obj is T & { + parserOpts: ( + cb: (_: never, parserOpts: Record) => unknown + ) => Record | undefined; +} { + return typeof obj.parserOpts === 'function'; +} + +export function validateConfig( + config: Record +): asserts config is { + formatter: string; + ignores?: ((commit: string) => boolean)[]; + defaultIgnores?: boolean; + [key: string]: unknown; +} { + if (!isObjectLike(config)) { + throw new Error('Invalid configuration, parserPreset must be an object'); + } + if (typeof config.formatter !== 'string') { + throw new Error('Invalid configuration, formatter must be a string'); + } + if (config.ignores && !Array.isArray(config.ignores)) { + throw new Error('Invalid configuration, ignores must ba an array'); + } + if ( + typeof config.defaultIgnores !== 'boolean' && + typeof config.defaultIgnores !== 'undefined' + ) { + throw new Error('Invalid configuration, defaultIgnores must ba true/false'); + } +} + +export function validateParser( + parser: unknown +): asserts parser is {name: string; path: string; [key: string]: unknown} { + if (!isObjectLike(parser)) { + throw new Error('Invalid configuration, parserPreset must be an object'); + } + if (typeof parser.name !== 'string') { + throw new Error('Invalid configuration, parserPreset must have a name'); + } + if (typeof parser.path !== 'string') { + throw new Error('Invalid configuration, parserPreset must have a name'); + } +} diff --git a/@commitlint/resolve-extends/package.json b/@commitlint/resolve-extends/package.json index 4a9e3e71f1..caab4a14a4 100644 --- a/@commitlint/resolve-extends/package.json +++ b/@commitlint/resolve-extends/package.json @@ -38,6 +38,7 @@ "@types/lodash": "^4.14.161" }, "dependencies": { + "@commitlint/types": "^11.0.0", "import-fresh": "^3.0.0", "lodash": "^4.17.19", "resolve-from": "^5.0.0", diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index f8c786c18b..351c3d7dd3 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -4,6 +4,7 @@ import 'resolve-global'; import resolveFrom from 'resolve-from'; import merge from 'lodash/merge'; import mergeWith from 'lodash/mergeWith'; +import {UserConfig} from '@commitlint/types'; const importFresh = require('import-fresh'); @@ -12,12 +13,6 @@ export interface ResolvedConfig { [key: string]: unknown; } -export interface ResolveExtendsConfig { - parserPreset?: unknown; - extends?: string | string[]; - [key: string]: unknown; -} - export interface ResolveExtendsContext { cwd?: string; parserPreset?: unknown; @@ -28,7 +23,7 @@ export interface ResolveExtendsContext { } export default function resolveExtends( - config: ResolveExtendsConfig = {}, + config: UserConfig = {}, context: ResolveExtendsContext = {} ) { const {extends: e} = config; @@ -46,7 +41,7 @@ export default function resolveExtends( } function loadExtends( - config: ResolveExtendsConfig = {}, + config: UserConfig = {}, context: ResolveExtendsContext = {} ): ResolvedConfig[] { const {extends: e} = config; diff --git a/@commitlint/resolve-extends/tsconfig.json b/@commitlint/resolve-extends/tsconfig.json index 49479bf34f..119e645565 100644 --- a/@commitlint/resolve-extends/tsconfig.json +++ b/@commitlint/resolve-extends/tsconfig.json @@ -6,5 +6,6 @@ "outDir": "./lib" }, "include": ["./src"], - "exclude": ["./src/**/*.test.ts", "./lib/**/*"] + "exclude": ["./src/**/*.test.ts", "./lib/**/*"], + "references": [{"path": "../types"}] } diff --git a/@commitlint/types/src/load.ts b/@commitlint/types/src/load.ts index fa08e9982e..ad0c2f56b9 100644 --- a/@commitlint/types/src/load.ts +++ b/@commitlint/types/src/load.ts @@ -20,10 +20,10 @@ export interface LoadOptions { } export interface UserConfig { - extends?: string[]; + extends?: string | string[]; formatter?: string; rules?: Partial; - parserPreset?: string | ParserPreset; + parserPreset?: string | ParserPreset | Promise; ignores?: ((commit: string) => boolean)[]; defaultIgnores?: boolean; plugins?: (string | Plugin)[]; @@ -46,9 +46,9 @@ export interface QualifiedConfig { extends: string[]; formatter: string; rules: QualifiedRules; - parserPreset: ParserPreset; - ignores: ((commit: string) => boolean)[]; - defaultIgnores: boolean; + parserPreset?: ParserPreset; + ignores?: ((commit: string) => boolean)[]; + defaultIgnores?: boolean; plugins: PluginRecords; helpUrl: string; } From a9477b83a266ba52c9318751fffb5b9415b286e8 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 10 Jan 2021 22:53:13 +0100 Subject: [PATCH 02/24] fix: simplify config resolution and fix issue #327 --- @commitlint/load/src/load.ts | 24 +++------ @commitlint/load/src/utils/validators.ts | 21 ++++++-- @commitlint/resolve-extends/src/index.test.ts | 47 ++++++++++++++++++ @commitlint/resolve-extends/src/index.ts | 49 +++++++++++++------ 4 files changed, 104 insertions(+), 37 deletions(-) diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index d03c18d0bd..8fd64d9f24 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -1,7 +1,7 @@ import Path from 'path'; import merge from 'lodash/merge'; -import union from 'lodash/union'; +import uniq from 'lodash/uniq'; import resolveFrom from 'resolve-from'; import executeRule from '@commitlint/execute-rule'; @@ -35,6 +35,8 @@ export default async function load( const config = pickConfig( merge( { + extends: [], + plugins: [], rules: {}, formatter: '@commitlint/format', helpUrl: @@ -57,7 +59,7 @@ export default async function load( } // Resolve extends key - const extended = resolveExtends(config, { + const extended = resolveExtends(config as any, { prefix: 'commitlint-config', cwd: base, parserPreset: config.parserPreset, @@ -66,13 +68,7 @@ export default async function load( validateConfig(extended); let plugins: PluginRecords = {}; - // TODO: this object merging should be done in resolveExtends - union( - // Read plugins from config - Array.isArray(config.plugins) ? config.plugins : [], - // Read plugins from extends - Array.isArray(extended.plugins) ? extended.plugins : [] - ).forEach((plugin) => { + uniq(extended.plugins || []).forEach((plugin) => { if (typeof plugin === 'string') { plugins = loadPlugin(plugins, plugin, process.env.DEBUG === 'true'); } else { @@ -82,10 +78,7 @@ export default async function load( const rules = ( await Promise.all( - Object.entries({ - ...(typeof extended.rules === 'object' ? extended.rules || {} : {}), - ...(typeof config.rules === 'object' ? config.rules || {} : {}), - }).map((entry) => executeRule(entry)) + Object.entries(extended.rules || {}).map((entry) => executeRule(entry)) ) ).reduce((registry, item) => { // type of `item` can be null, but Object.entries always returns key pair @@ -111,9 +104,6 @@ export default async function load( defaultIgnores: extended.defaultIgnores, plugins: plugins, rules: rules, - helpUrl: - typeof extended.helpUrl === 'string' - ? extended.helpUrl - : 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint', + helpUrl: extended.helpUrl, }; } diff --git a/@commitlint/load/src/utils/validators.ts b/@commitlint/load/src/utils/validators.ts index f26da6aaf5..3027707da2 100644 --- a/@commitlint/load/src/utils/validators.ts +++ b/@commitlint/load/src/utils/validators.ts @@ -1,3 +1,5 @@ +import {Plugin, RulesConfig} from '@commitlint/types'; + export function isObjectLike(obj: unknown): obj is Record { return Boolean(obj) && typeof obj === 'object'; // typeof null === 'object' } @@ -25,22 +27,33 @@ export function validateConfig( formatter: string; ignores?: ((commit: string) => boolean)[]; defaultIgnores?: boolean; + plugins?: (Plugin | string)[]; + rules: Partial; + helpUrl: string; [key: string]: unknown; } { if (!isObjectLike(config)) { - throw new Error('Invalid configuration, parserPreset must be an object'); + throw new Error('Invalid configuration, `parserPreset` must be an object'); } if (typeof config.formatter !== 'string') { - throw new Error('Invalid configuration, formatter must be a string'); + throw new Error('Invalid configuration, `formatter` must be a string'); } if (config.ignores && !Array.isArray(config.ignores)) { - throw new Error('Invalid configuration, ignores must ba an array'); + throw new Error('Invalid configuration, `ignores` must ba an array'); + } + if (config.plugins && !Array.isArray(config.plugins)) { + throw new Error('Invalid configuration, `plugins` must ba an array'); } if ( typeof config.defaultIgnores !== 'boolean' && typeof config.defaultIgnores !== 'undefined' ) { - throw new Error('Invalid configuration, defaultIgnores must ba true/false'); + throw new Error( + 'Invalid configuration, `defaultIgnores` must ba true/false' + ); + } + if (typeof config.helpUrl !== 'string') { + throw new Error('Invalid configuration, `helpUrl` must be a string'); } } diff --git a/@commitlint/resolve-extends/src/index.test.ts b/@commitlint/resolve-extends/src/index.test.ts index b386d001c4..6a0da9e303 100644 --- a/@commitlint/resolve-extends/src/index.test.ts +++ b/@commitlint/resolve-extends/src/index.test.ts @@ -348,3 +348,50 @@ test('should fall back to conventional-changelog-lint-config prefix', () => { }, }); }); + +// https://github.com/conventional-changelog/commitlint/issues/327 +test('parserPreset should resolve correctly in extended configuration', () => { + const input = {extends: ['extender-name'], zero: 'root'}; + + const require = (id: string) => { + switch (id) { + case 'extender-name': + return { + extends: ['recursive-extender-name'], + parserPreset: { + parserOpts: { + issuePrefixes: ['#', '!', '&', 'no-references'], + referenceActions: null, + }, + }, + }; + case 'recursive-extender-name': + return { + parserPreset: { + parserOpts: { + issuePrefixes: ['#', '!'], + }, + }, + }; + default: + return {}; + } + }; + + const ctx = {resolve: id, require: jest.fn(require)} as ResolveExtendsContext; + + const actual = resolveExtends(input, ctx); + + const expected = { + extends: ['extender-name'], + parserPreset: { + parserOpts: { + issuePrefixes: ['#', '!', '&', 'no-references'], + referenceActions: null, + }, + }, + zero: 'root', + }; + + expect(actual).toEqual(expected); +}); diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index 351c3d7dd3..e6318860c6 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -4,10 +4,14 @@ import 'resolve-global'; import resolveFrom from 'resolve-from'; import merge from 'lodash/merge'; import mergeWith from 'lodash/mergeWith'; -import {UserConfig} from '@commitlint/types'; const importFresh = require('import-fresh'); +export interface ResolveExtendsConfig { + extends?: string | string[]; + [key: string]: unknown; +} + export interface ResolvedConfig { parserPreset?: unknown; [key: string]: unknown; @@ -22,32 +26,45 @@ export interface ResolveExtendsContext { require?(id: string): T; } +function mergeStrategy(objValue: unknown, srcValue: unknown, key: string) { + if (key === 'parserPreset') { + if (typeof srcValue !== 'object') { + return objValue; + } + } else if (key === 'rules') { + if (typeof objValue !== 'object') { + return srcValue; + } + } else if (key === 'plugins') { + if (!Array.isArray(objValue)) { + return srcValue; + } + } else if (Array.isArray(objValue)) { + return srcValue; + } +} + export default function resolveExtends( - config: UserConfig = {}, + config: ResolveExtendsConfig = {}, context: ResolveExtendsContext = {} -) { +): ResolvedConfig { const {extends: e} = config; - const extended = loadExtends(config, context).reduce( - (r, {extends: _, ...c}) => - mergeWith(r, c, (objValue, srcValue) => { - if (Array.isArray(objValue)) { - return srcValue; - } - }), + const extended = loadExtends(config, context); + extended.push(config); + return extended.reduce( + (r, {extends: _, ...c}) => mergeWith(r, c, mergeStrategy), e ? {extends: e} : {} ); - - return merge({}, extended, config); } function loadExtends( - config: UserConfig = {}, + config: ResolveExtendsConfig = {}, context: ResolveExtendsContext = {} ): ResolvedConfig[] { const {extends: e} = config; - const ext = e ? (Array.isArray(e) ? e : [e]) : []; + const ext = e ? (Array.isArray(e) ? [...e] : [e]) : []; - return ext.reduce((configs, raw) => { + return ext.reverse().reduce((configs, raw) => { const load = context.require || require; const resolved = resolveConfig(raw, context); const c = load(resolved); @@ -73,7 +90,7 @@ function loadExtends( config.parserPreset = parserPreset; } - return [...configs, ...loadExtends(c, ctx), c]; + return [...loadExtends(c, ctx), c, ...configs]; }, []); } From 01ef3619c63d96470fd295ee3a457246263e1d0a Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 10 Jan 2021 22:59:48 +0100 Subject: [PATCH 03/24] fix: remove no longer needed function --- @commitlint/resolve-extends/src/index.ts | 37 ++++++++++++------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index e6318860c6..c27b4d7337 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -26,24 +26,6 @@ export interface ResolveExtendsContext { require?(id: string): T; } -function mergeStrategy(objValue: unknown, srcValue: unknown, key: string) { - if (key === 'parserPreset') { - if (typeof srcValue !== 'object') { - return objValue; - } - } else if (key === 'rules') { - if (typeof objValue !== 'object') { - return srcValue; - } - } else if (key === 'plugins') { - if (!Array.isArray(objValue)) { - return srcValue; - } - } else if (Array.isArray(objValue)) { - return srcValue; - } -} - export default function resolveExtends( config: ResolveExtendsConfig = {}, context: ResolveExtendsContext = {} @@ -52,7 +34,24 @@ export default function resolveExtends( const extended = loadExtends(config, context); extended.push(config); return extended.reduce( - (r, {extends: _, ...c}) => mergeWith(r, c, mergeStrategy), + (r, {extends: _, ...c}) => + mergeWith(r, c, (objValue, srcValue, key) => { + if (key === 'parserPreset') { + if (typeof srcValue !== 'object') { + return objValue; + } + } else if (key === 'rules') { + if (typeof objValue !== 'object') { + return srcValue; + } + } else if (key === 'plugins') { + if (!Array.isArray(objValue)) { + return srcValue; + } + } else if (Array.isArray(objValue)) { + return srcValue; + } + }), e ? {extends: e} : {} ); } From e09fc0121172629de1e7481a557d9eae501f57c9 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 10 Jan 2021 23:09:41 +0100 Subject: [PATCH 04/24] fix: disable some unwanted validations --- @commitlint/load/src/utils/load-parser-opts.ts | 12 +++++------- @commitlint/load/src/utils/validators.ts | 14 -------------- @commitlint/types/src/load.ts | 4 ++-- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index 5380f529ba..f168fd642a 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -1,10 +1,5 @@ import {ParserPreset} from '@commitlint/types'; -import { - isObjectLike, - isParserOptsFunction, - isPromiseLike, - validateParser, -} from './validators'; +import {isObjectLike, isParserOptsFunction, isPromiseLike} from './validators'; export async function loadParser( pendingParser: unknown @@ -15,7 +10,9 @@ export async function loadParser( // Await for the module, loaded with require const parser = await pendingParser; - validateParser(parser); + if (!isObjectLike(parser)) { + throw new Error('Invalid configuration, `parserPreset` must be an object'); + } // Await parser opts if applicable if (isPromiseLike(parser.parserOpts)) { @@ -26,6 +23,7 @@ export async function loadParser( // Create parser opts from factory if ( isParserOptsFunction(parser) && + typeof parser.name === 'string' && parser.name.startsWith('conventional-changelog-') ) { return new Promise((resolve) => { diff --git a/@commitlint/load/src/utils/validators.ts b/@commitlint/load/src/utils/validators.ts index 3027707da2..7b76e17b88 100644 --- a/@commitlint/load/src/utils/validators.ts +++ b/@commitlint/load/src/utils/validators.ts @@ -56,17 +56,3 @@ export function validateConfig( throw new Error('Invalid configuration, `helpUrl` must be a string'); } } - -export function validateParser( - parser: unknown -): asserts parser is {name: string; path: string; [key: string]: unknown} { - if (!isObjectLike(parser)) { - throw new Error('Invalid configuration, parserPreset must be an object'); - } - if (typeof parser.name !== 'string') { - throw new Error('Invalid configuration, parserPreset must have a name'); - } - if (typeof parser.path !== 'string') { - throw new Error('Invalid configuration, parserPreset must have a name'); - } -} diff --git a/@commitlint/types/src/load.ts b/@commitlint/types/src/load.ts index ad0c2f56b9..2ae8d689d1 100644 --- a/@commitlint/types/src/load.ts +++ b/@commitlint/types/src/load.ts @@ -54,7 +54,7 @@ export interface QualifiedConfig { } export interface ParserPreset { - name: string; - path: string; + name?: string; + path?: string; parserOpts?: unknown; } From 26612a8738b92aae59fad24dbcea697f38ce1b56 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 10 Jan 2021 23:35:28 +0100 Subject: [PATCH 05/24] fix: improve config validation --- @commitlint/load/src/load.ts | 12 +++-- .../load/src/utils/load-parser-opts.ts | 13 +++-- @commitlint/load/src/utils/validators.ts | 47 +++++++++++++------ 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index 8fd64d9f24..fc9231c140 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -38,9 +38,6 @@ export default async function load( extends: [], plugins: [], rules: {}, - formatter: '@commitlint/format', - helpUrl: - 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint', }, loaded ? loaded.config : null, seed @@ -67,6 +64,15 @@ export default async function load( validateConfig(extended); + if (!extended.formatter) { + extended.formatter = '@commitlint/format'; + } + + if (!extended.helpUrl) { + extended.helpUrl = + 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'; + } + let plugins: PluginRecords = {}; uniq(extended.plugins || []).forEach((plugin) => { if (typeof plugin === 'string') { diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index f168fd642a..9978f54172 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -1,5 +1,10 @@ import {ParserPreset} from '@commitlint/types'; -import {isObjectLike, isParserOptsFunction, isPromiseLike} from './validators'; +import { + isObjectLike, + isParserOptsFunction, + isPromiseLike, + validateParser, +} from './validators'; export async function loadParser( pendingParser: unknown @@ -10,9 +15,7 @@ export async function loadParser( // Await for the module, loaded with require const parser = await pendingParser; - if (!isObjectLike(parser)) { - throw new Error('Invalid configuration, `parserPreset` must be an object'); - } + validateParser(parser); // Await parser opts if applicable if (isPromiseLike(parser.parserOpts)) { @@ -23,7 +26,7 @@ export async function loadParser( // Create parser opts from factory if ( isParserOptsFunction(parser) && - typeof parser.name === 'string' && + parser.name && parser.name.startsWith('conventional-changelog-') ) { return new Promise((resolve) => { diff --git a/@commitlint/load/src/utils/validators.ts b/@commitlint/load/src/utils/validators.ts index 7b76e17b88..40c44f9245 100644 --- a/@commitlint/load/src/utils/validators.ts +++ b/@commitlint/load/src/utils/validators.ts @@ -1,4 +1,4 @@ -import {Plugin, RulesConfig} from '@commitlint/types'; +import {UserConfig} from '@commitlint/types'; export function isObjectLike(obj: unknown): obj is Record { return Boolean(obj) && typeof obj === 'object'; // typeof null === 'object' @@ -21,21 +21,22 @@ export function isParserOptsFunction>( return typeof obj.parserOpts === 'function'; } -export function validateConfig( - config: Record -): asserts config is { - formatter: string; - ignores?: ((commit: string) => boolean)[]; - defaultIgnores?: boolean; - plugins?: (Plugin | string)[]; - rules: Partial; - helpUrl: string; - [key: string]: unknown; -} { +export function validateConfig>( + config: T +): asserts config is Omit & T { if (!isObjectLike(config)) { throw new Error('Invalid configuration, `parserPreset` must be an object'); } - if (typeof config.formatter !== 'string') { + if ( + config.extends && + typeof config.extends !== 'string' && + !Array.isArray(config.extends) + ) { + throw new Error( + 'Invalid configuration, `extends` must be a array or string' + ); + } + if (config.formatter && typeof config.formatter !== 'string') { throw new Error('Invalid configuration, `formatter` must be a string'); } if (config.ignores && !Array.isArray(config.ignores)) { @@ -44,7 +45,11 @@ export function validateConfig( if (config.plugins && !Array.isArray(config.plugins)) { throw new Error('Invalid configuration, `plugins` must ba an array'); } + if (config.rules && typeof config.rules !== 'object') { + throw new Error('Invalid configuration, `rules` must ba an object'); + } if ( + config.defaultIgnores && typeof config.defaultIgnores !== 'boolean' && typeof config.defaultIgnores !== 'undefined' ) { @@ -52,7 +57,21 @@ export function validateConfig( 'Invalid configuration, `defaultIgnores` must ba true/false' ); } - if (typeof config.helpUrl !== 'string') { + if (config.helpUrl && typeof config.helpUrl !== 'string') { throw new Error('Invalid configuration, `helpUrl` must be a string'); } } + +export function validateParser( + parser: unknown +): asserts parser is {name?: string; path?: string; [key: string]: unknown} { + if (!isObjectLike(parser)) { + throw new Error('Invalid configuration, `parserPreset` must be an object'); + } + if (parser.name && typeof parser.name !== 'string') { + throw new Error('Invalid configuration, `parserPreset` must have a name'); + } + if (parser.path && typeof parser.path !== 'string') { + throw new Error('Invalid configuration, `parserPreset` must have a name'); + } +} From fb7064c49eef0088f7e82d69fc2bdb47ad21a488 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 10 Jan 2021 23:43:10 +0100 Subject: [PATCH 06/24] fix: remove redundant validation --- @commitlint/load/src/utils/validators.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/@commitlint/load/src/utils/validators.ts b/@commitlint/load/src/utils/validators.ts index 40c44f9245..b0219396e5 100644 --- a/@commitlint/load/src/utils/validators.ts +++ b/@commitlint/load/src/utils/validators.ts @@ -48,11 +48,7 @@ export function validateConfig>( if (config.rules && typeof config.rules !== 'object') { throw new Error('Invalid configuration, `rules` must ba an object'); } - if ( - config.defaultIgnores && - typeof config.defaultIgnores !== 'boolean' && - typeof config.defaultIgnores !== 'undefined' - ) { + if (config.defaultIgnores && typeof config.defaultIgnores !== 'boolean') { throw new Error( 'Invalid configuration, `defaultIgnores` must ba true/false' ); From b0d482972b83c8e930bb56feb54f30a2d756355c Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 10 Jan 2021 23:48:45 +0100 Subject: [PATCH 07/24] fix: use reduceRight instead of reverse --- @commitlint/resolve-extends/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index c27b4d7337..051824365f 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -61,9 +61,9 @@ function loadExtends( context: ResolveExtendsContext = {} ): ResolvedConfig[] { const {extends: e} = config; - const ext = e ? (Array.isArray(e) ? [...e] : [e]) : []; + const ext = e ? (Array.isArray(e) ? e : [e]) : []; - return ext.reverse().reduce((configs, raw) => { + return ext.reduceRight((configs, raw) => { const load = context.require || require; const resolved = resolveConfig(raw, context); const c = load(resolved); From ff67ad700c3115593c0fcc1ae73073beb6e7218c Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 11 Jan 2021 00:02:55 +0100 Subject: [PATCH 08/24] fix: rollback some code --- @commitlint/load/src/load.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index fc9231c140..01216377dc 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -64,15 +64,6 @@ export default async function load( validateConfig(extended); - if (!extended.formatter) { - extended.formatter = '@commitlint/format'; - } - - if (!extended.helpUrl) { - extended.helpUrl = - 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'; - } - let plugins: PluginRecords = {}; uniq(extended.plugins || []).forEach((plugin) => { if (typeof plugin === 'string') { @@ -93,6 +84,15 @@ export default async function load( return registry; }, {}); + if (!extended.formatter) { + extended.formatter = '@commitlint/format'; + } + + if (!extended.helpUrl) { + extended.helpUrl = + 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'; + } + return { // TODO: check if this is still necessary with the new factory based conventional changelog parsers // TODO: should this function return this? as those values are already resolved From 3bd8384abde9824c91d329ba8c1ca10989d3cbe5 Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 11 Jan 2021 00:13:57 +0100 Subject: [PATCH 09/24] fix: drop invalid type casts --- @commitlint/load/src/load.ts | 2 +- @commitlint/load/src/utils/pick-config.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index 01216377dc..ae53c4b0f9 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -56,7 +56,7 @@ export default async function load( } // Resolve extends key - const extended = resolveExtends(config as any, { + const extended = resolveExtends(config, { prefix: 'commitlint-config', cwd: base, parserPreset: config.parserPreset, diff --git a/@commitlint/load/src/utils/pick-config.ts b/@commitlint/load/src/utils/pick-config.ts index e5e01181c0..757ed0f702 100644 --- a/@commitlint/load/src/utils/pick-config.ts +++ b/@commitlint/load/src/utils/pick-config.ts @@ -1,7 +1,6 @@ -import {UserConfig} from '@commitlint/types'; import pick from 'lodash/pick'; -export const pickConfig = (input: unknown): UserConfig => +export const pickConfig = (input: unknown): Record => pick( input, 'extends', From 7fcf5e08fd5861913d0597911d97d87b7b874fd3 Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 11 Jan 2021 00:15:03 +0100 Subject: [PATCH 10/24] fix: rollback unnecessary changes --- @commitlint/resolve-extends/src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index 051824365f..682dcc9fdc 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -7,13 +7,13 @@ import mergeWith from 'lodash/mergeWith'; const importFresh = require('import-fresh'); -export interface ResolveExtendsConfig { - extends?: string | string[]; +export interface ResolvedConfig { + parserPreset?: unknown; [key: string]: unknown; } -export interface ResolvedConfig { - parserPreset?: unknown; +export interface ResolveExtendsConfig { + extends?: string | string[]; [key: string]: unknown; } From 5a105898e204c68d6d5e64f8f6e48cdc68405d1e Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 15 Jan 2021 22:12:08 +0100 Subject: [PATCH 11/24] fix: rollback config validation --- @commitlint/load/src/load.ts | 11 +-- .../load/src/utils/load-parser-opts.ts | 39 ++++++---- @commitlint/load/src/utils/validators.ts | 73 ------------------- 3 files changed, 30 insertions(+), 93 deletions(-) delete mode 100644 @commitlint/load/src/utils/validators.ts diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index ae53c4b0f9..196c18b13a 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -16,9 +16,8 @@ import { import loadPlugin from './utils/load-plugin'; import {loadConfig} from './utils/load-config'; -import {loadParser} from './utils/load-parser-opts'; +import {loadParserOpts} from './utils/load-parser-opts'; import {pickConfig} from './utils/pick-config'; -import {validateConfig} from './utils/validators'; export default async function load( seed: UserConfig = {}, @@ -56,13 +55,11 @@ export default async function load( } // Resolve extends key - const extended = resolveExtends(config, { + const extended = (resolveExtends(config, { prefix: 'commitlint-config', cwd: base, parserPreset: config.parserPreset, - }); - - validateConfig(extended); + }) as unknown) as UserConfig; let plugins: PluginRecords = {}; uniq(extended.plugins || []).forEach((plugin) => { @@ -105,7 +102,7 @@ export default async function load( formatter: resolveFrom.silent(base, extended.formatter) || extended.formatter, // Resolve parser-opts from preset - parserPreset: await loadParser(extended.parserPreset), + parserPreset: await loadParserOpts(extended.parserPreset), ignores: extended.ignores, defaultIgnores: extended.defaultIgnores, plugins: plugins, diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index 9978f54172..3b78db4b25 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -1,22 +1,35 @@ import {ParserPreset} from '@commitlint/types'; -import { - isObjectLike, - isParserOptsFunction, - isPromiseLike, - validateParser, -} from './validators'; - -export async function loadParser( - pendingParser: unknown + +function isObjectLike(obj: unknown): obj is Record { + return Boolean(obj) && typeof obj === 'object'; // typeof null === 'object' +} + +function isPromiseLike(obj: unknown): obj is Promise { + return ( + (typeof obj === 'object' || typeof obj === 'function') && + typeof (obj as any).then === 'function' + ); +} + +function isParserOptsFunction( + obj: T +): obj is T & { + parserOpts: ( + cb: (_: never, parserOpts: Record) => unknown + ) => Record | undefined; +} { + return typeof obj.parserOpts === 'function'; +} + +export async function loadParserOpts( + pendingParser: string | ParserPreset | Promise | undefined ): Promise { - if (!pendingParser) { + if (!pendingParser || typeof pendingParser === 'string') { return undefined; } // Await for the module, loaded with require const parser = await pendingParser; - validateParser(parser); - // Await parser opts if applicable if (isPromiseLike(parser.parserOpts)) { parser.parserOpts = ((await parser.parserOpts) as any).parserOpts; @@ -26,7 +39,7 @@ export async function loadParser( // Create parser opts from factory if ( isParserOptsFunction(parser) && - parser.name && + typeof parser.name === 'string' && parser.name.startsWith('conventional-changelog-') ) { return new Promise((resolve) => { diff --git a/@commitlint/load/src/utils/validators.ts b/@commitlint/load/src/utils/validators.ts deleted file mode 100644 index b0219396e5..0000000000 --- a/@commitlint/load/src/utils/validators.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {UserConfig} from '@commitlint/types'; - -export function isObjectLike(obj: unknown): obj is Record { - return Boolean(obj) && typeof obj === 'object'; // typeof null === 'object' -} - -export function isPromiseLike(obj: unknown): obj is Promise { - return ( - (typeof obj === 'object' || typeof obj === 'function') && - typeof (obj as any).then === 'function' - ); -} - -export function isParserOptsFunction>( - obj: T -): obj is T & { - parserOpts: ( - cb: (_: never, parserOpts: Record) => unknown - ) => Record | undefined; -} { - return typeof obj.parserOpts === 'function'; -} - -export function validateConfig>( - config: T -): asserts config is Omit & T { - if (!isObjectLike(config)) { - throw new Error('Invalid configuration, `parserPreset` must be an object'); - } - if ( - config.extends && - typeof config.extends !== 'string' && - !Array.isArray(config.extends) - ) { - throw new Error( - 'Invalid configuration, `extends` must be a array or string' - ); - } - if (config.formatter && typeof config.formatter !== 'string') { - throw new Error('Invalid configuration, `formatter` must be a string'); - } - if (config.ignores && !Array.isArray(config.ignores)) { - throw new Error('Invalid configuration, `ignores` must ba an array'); - } - if (config.plugins && !Array.isArray(config.plugins)) { - throw new Error('Invalid configuration, `plugins` must ba an array'); - } - if (config.rules && typeof config.rules !== 'object') { - throw new Error('Invalid configuration, `rules` must ba an object'); - } - if (config.defaultIgnores && typeof config.defaultIgnores !== 'boolean') { - throw new Error( - 'Invalid configuration, `defaultIgnores` must ba true/false' - ); - } - if (config.helpUrl && typeof config.helpUrl !== 'string') { - throw new Error('Invalid configuration, `helpUrl` must be a string'); - } -} - -export function validateParser( - parser: unknown -): asserts parser is {name?: string; path?: string; [key: string]: unknown} { - if (!isObjectLike(parser)) { - throw new Error('Invalid configuration, `parserPreset` must be an object'); - } - if (parser.name && typeof parser.name !== 'string') { - throw new Error('Invalid configuration, `parserPreset` must have a name'); - } - if (parser.path && typeof parser.path !== 'string') { - throw new Error('Invalid configuration, `parserPreset` must have a name'); - } -} From b2af2e0cbf1f30bc7d332bdcf6647a9abb6d25f9 Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 15 Jan 2021 22:25:39 +0100 Subject: [PATCH 12/24] fix: add missing type-guards and restore order --- @commitlint/load/src/load.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index 196c18b13a..d801afbf0d 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -70,6 +70,15 @@ export default async function load( } }); + if (!extended.formatter || typeof extended.formatter !== 'string') { + extended.formatter = '@commitlint/format'; + } + + if (!extended.helpUrl || typeof extended.helpUrl !== 'string') { + extended.helpUrl = + 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'; + } + const rules = ( await Promise.all( Object.entries(extended.rules || {}).map((entry) => executeRule(entry)) @@ -81,15 +90,6 @@ export default async function load( return registry; }, {}); - if (!extended.formatter) { - extended.formatter = '@commitlint/format'; - } - - if (!extended.helpUrl) { - extended.helpUrl = - 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'; - } - return { // TODO: check if this is still necessary with the new factory based conventional changelog parsers // TODO: should this function return this? as those values are already resolved From a165e15f2ceb2c1cc43a7474856e6d274bd239c5 Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 15 Jan 2021 22:27:18 +0100 Subject: [PATCH 13/24] fix: one more order change --- @commitlint/load/src/load.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index d801afbf0d..18d77a22cc 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -61,15 +61,6 @@ export default async function load( parserPreset: config.parserPreset, }) as unknown) as UserConfig; - let plugins: PluginRecords = {}; - uniq(extended.plugins || []).forEach((plugin) => { - if (typeof plugin === 'string') { - plugins = loadPlugin(plugins, plugin, process.env.DEBUG === 'true'); - } else { - plugins.local = plugin; - } - }); - if (!extended.formatter || typeof extended.formatter !== 'string') { extended.formatter = '@commitlint/format'; } @@ -79,6 +70,15 @@ export default async function load( 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'; } + let plugins: PluginRecords = {}; + uniq(extended.plugins || []).forEach((plugin) => { + if (typeof plugin === 'string') { + plugins = loadPlugin(plugins, plugin, process.env.DEBUG === 'true'); + } else { + plugins.local = plugin; + } + }); + const rules = ( await Promise.all( Object.entries(extended.rules || {}).map((entry) => executeRule(entry)) From 28185b6a1ee258ae058f9468574207766e65b09c Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 15 Jan 2021 22:30:19 +0100 Subject: [PATCH 14/24] fix: add one more missing type guard --- @commitlint/load/src/load.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/@commitlint/load/src/load.ts b/@commitlint/load/src/load.ts index 18d77a22cc..4ff22804b4 100644 --- a/@commitlint/load/src/load.ts +++ b/@commitlint/load/src/load.ts @@ -71,13 +71,15 @@ export default async function load( } let plugins: PluginRecords = {}; - uniq(extended.plugins || []).forEach((plugin) => { - if (typeof plugin === 'string') { - plugins = loadPlugin(plugins, plugin, process.env.DEBUG === 'true'); - } else { - plugins.local = plugin; - } - }); + if (Array.isArray(extended.plugins)) { + uniq(extended.plugins || []).forEach((plugin) => { + if (typeof plugin === 'string') { + plugins = loadPlugin(plugins, plugin, process.env.DEBUG === 'true'); + } else { + plugins.local = plugin; + } + }); + } const rules = ( await Promise.all( From 0c75eb941fea780909cfdd4631eb64207bfa4ad8 Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 15 Jan 2021 23:58:59 +0100 Subject: [PATCH 15/24] fix: remove unused types reference --- @commitlint/resolve-extends/package.json | 1 - @commitlint/resolve-extends/tsconfig.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/@commitlint/resolve-extends/package.json b/@commitlint/resolve-extends/package.json index caab4a14a4..4a9e3e71f1 100644 --- a/@commitlint/resolve-extends/package.json +++ b/@commitlint/resolve-extends/package.json @@ -38,7 +38,6 @@ "@types/lodash": "^4.14.161" }, "dependencies": { - "@commitlint/types": "^11.0.0", "import-fresh": "^3.0.0", "lodash": "^4.17.19", "resolve-from": "^5.0.0", diff --git a/@commitlint/resolve-extends/tsconfig.json b/@commitlint/resolve-extends/tsconfig.json index 119e645565..49479bf34f 100644 --- a/@commitlint/resolve-extends/tsconfig.json +++ b/@commitlint/resolve-extends/tsconfig.json @@ -6,6 +6,5 @@ "outDir": "./lib" }, "include": ["./src"], - "exclude": ["./src/**/*.test.ts", "./lib/**/*"], - "references": [{"path": "../types"}] + "exclude": ["./src/**/*.test.ts", "./lib/**/*"] } From 45d1e2595aab63251aff14d1d0ad06123cbe3b1d Mon Sep 17 00:00:00 2001 From: Armano Date: Sat, 16 Jan 2021 02:24:54 +0100 Subject: [PATCH 16/24] fix: add additional unit tests --- @commitlint/resolve-extends/src/index.test.ts | 69 +++++++++++++++++++ @commitlint/resolve-extends/src/index.ts | 4 +- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.test.ts b/@commitlint/resolve-extends/src/index.test.ts index 6a0da9e303..fd71e16036 100644 --- a/@commitlint/resolve-extends/src/index.test.ts +++ b/@commitlint/resolve-extends/src/index.test.ts @@ -349,6 +349,75 @@ test('should fall back to conventional-changelog-lint-config prefix', () => { }); }); +test('plugins should be merged correctly', () => { + const input = {extends: ['extender-name'], zero: 'root'}; + + const require = (id: string) => { + switch (id) { + case 'extender-name': + return {extends: ['recursive-extender-name'], plugins: ['test']}; + case 'recursive-extender-name': + return { + extends: ['second-recursive-extender-name'], + plugins: ['test2'], + }; + case 'second-recursive-extender-name': + return {plugins: ['test3']}; + default: + return {}; + } + }; + + const ctx = {resolve: id, require: jest.fn(require)} as ResolveExtendsContext; + + const actual = resolveExtends(input, ctx); + + const expected = { + extends: ['extender-name'], + plugins: ['test', 'test2', 'test3'], + zero: 'root', + }; + + expect(actual).toEqual(expected); +}); + +test('rules should be merged correctly', () => { + const input = {extends: ['extender-name'], rules: {test1: ['base', '1']}}; + + const require = (id: string) => { + switch (id) { + case 'extender-name': + return { + extends: ['recursive-extender-name'], + rules: {test2: [id, '2']}, + }; + case 'recursive-extender-name': + return { + extends: ['second-recursive-extender-name'], + rules: {test1: [id, '3']}, + }; + case 'second-recursive-extender-name': + return {rules: {test2: [id, '4']}}; + default: + return {}; + } + }; + + const ctx = {resolve: id, require: jest.fn(require)} as ResolveExtendsContext; + + const actual = resolveExtends(input, ctx); + + const expected = { + extends: ['extender-name'], + rules: { + test1: ['base', '1'], + test2: ['extender-name', '2'], + }, + }; + + expect(actual).toEqual(expected); +}); + // https://github.com/conventional-changelog/commitlint/issues/327 test('parserPreset should resolve correctly in extended configuration', () => { const input = {extends: ['extender-name'], zero: 'root'}; diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index 682dcc9fdc..9ba0e0eeb0 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -45,8 +45,8 @@ export default function resolveExtends( return srcValue; } } else if (key === 'plugins') { - if (!Array.isArray(objValue)) { - return srcValue; + if (Array.isArray(objValue)) { + return srcValue.concat(objValue); } } else if (Array.isArray(objValue)) { return srcValue; From 75d9011c891f9f1892501b0357b29b866ef02e3f Mon Sep 17 00:00:00 2001 From: Armano Date: Sat, 16 Jan 2021 02:33:58 +0100 Subject: [PATCH 17/24] fix: add additional regression tests - remove also unnecessary type check --- @commitlint/resolve-extends/src/index.test.ts | 43 ++++++++++++++++--- @commitlint/resolve-extends/src/index.ts | 6 +-- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.test.ts b/@commitlint/resolve-extends/src/index.test.ts index fd71e16036..b49d7f9d9a 100644 --- a/@commitlint/resolve-extends/src/index.test.ts +++ b/@commitlint/resolve-extends/src/index.test.ts @@ -321,11 +321,7 @@ test('should fall back to conventional-changelog-lint-config prefix', () => { const require = (id: string) => { switch (id) { case 'conventional-changelog-lint-config-extender-name': - return { - rules: { - fallback: true, - }, - }; + return {rules: {fallback: true}}; default: return {}; } @@ -435,13 +431,46 @@ test('parserPreset should resolve correctly in extended configuration', () => { }, }; case 'recursive-extender-name': + return {parserPreset: {parserOpts: {issuePrefixes: ['#', '!']}}}; + default: + return {}; + } + }; + + const ctx = {resolve: id, require: jest.fn(require)} as ResolveExtendsContext; + + const actual = resolveExtends(input, ctx); + + const expected = { + extends: ['extender-name'], + parserPreset: { + parserOpts: { + issuePrefixes: ['#', '!', '&', 'no-references'], + referenceActions: null, + }, + }, + zero: 'root', + }; + + expect(actual).toEqual(expected); +}); + +test('parserPreset should be merged correctly', () => { + const input = {extends: ['extender-name'], zero: 'root'}; + + const require = (id: string) => { + switch (id) { + case 'extender-name': return { + extends: ['recursive-extender-name'], parserPreset: { parserOpts: { - issuePrefixes: ['#', '!'], + referenceActions: null, }, }, }; + case 'recursive-extender-name': + return {parserPreset: {parserOpts: {issuePrefixes: ['#', '!']}}}; default: return {}; } @@ -455,7 +484,7 @@ test('parserPreset should resolve correctly in extended configuration', () => { extends: ['extender-name'], parserPreset: { parserOpts: { - issuePrefixes: ['#', '!', '&', 'no-references'], + issuePrefixes: ['#', '!'], referenceActions: null, }, }, diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index 9ba0e0eeb0..5917a4e802 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -36,11 +36,7 @@ export default function resolveExtends( return extended.reduce( (r, {extends: _, ...c}) => mergeWith(r, c, (objValue, srcValue, key) => { - if (key === 'parserPreset') { - if (typeof srcValue !== 'object') { - return objValue; - } - } else if (key === 'rules') { + if (key === 'rules') { if (typeof objValue !== 'object') { return srcValue; } From bd6e95848a40efd4c6f9e0f0f8a3f88704e15c6b Mon Sep 17 00:00:00 2001 From: Armano Date: Sat, 16 Jan 2021 02:35:13 +0100 Subject: [PATCH 18/24] fix: remove more unnecessary code changes --- @commitlint/resolve-extends/src/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index 5917a4e802..d1a453694e 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -36,11 +36,7 @@ export default function resolveExtends( return extended.reduce( (r, {extends: _, ...c}) => mergeWith(r, c, (objValue, srcValue, key) => { - if (key === 'rules') { - if (typeof objValue !== 'object') { - return srcValue; - } - } else if (key === 'plugins') { + if (key === 'plugins') { if (Array.isArray(objValue)) { return srcValue.concat(objValue); } From 9303234d2b9a1cc9b9f286d6ac1ab43690924729 Mon Sep 17 00:00:00 2001 From: Armano Date: Sat, 16 Jan 2021 02:38:43 +0100 Subject: [PATCH 19/24] fix: correct order of merging plugins --- @commitlint/resolve-extends/src/index.test.ts | 2 +- @commitlint/resolve-extends/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.test.ts b/@commitlint/resolve-extends/src/index.test.ts index b49d7f9d9a..98099af3dd 100644 --- a/@commitlint/resolve-extends/src/index.test.ts +++ b/@commitlint/resolve-extends/src/index.test.ts @@ -370,7 +370,7 @@ test('plugins should be merged correctly', () => { const expected = { extends: ['extender-name'], - plugins: ['test', 'test2', 'test3'], + plugins: ['test3', 'test2', 'test'], zero: 'root', }; diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index d1a453694e..597a9db556 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -38,7 +38,7 @@ export default function resolveExtends( mergeWith(r, c, (objValue, srcValue, key) => { if (key === 'plugins') { if (Array.isArray(objValue)) { - return srcValue.concat(objValue); + return objValue.concat(srcValue); } } else if (Array.isArray(objValue)) { return srcValue; From 1e2060700f4e03447e9179523c97b6909bb117e3 Mon Sep 17 00:00:00 2001 From: Armano Date: Sat, 16 Jan 2021 02:41:31 +0100 Subject: [PATCH 20/24] fix: add missing type check --- @commitlint/load/src/utils/load-parser-opts.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index 3b78db4b25..13b36557f6 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -29,6 +29,9 @@ export async function loadParserOpts( } // Await for the module, loaded with require const parser = await pendingParser; + if (typeof pendingParser !== 'object') { + return undefined; + } // Await parser opts if applicable if (isPromiseLike(parser.parserOpts)) { From b7507437057e9b585579990742a37744e117bb96 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 17 Jan 2021 05:45:21 +0100 Subject: [PATCH 21/24] fix: remove invalid type check --- @commitlint/load/src/utils/load-parser-opts.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index 13b36557f6..104d3392a4 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -5,10 +5,7 @@ function isObjectLike(obj: unknown): obj is Record { } function isPromiseLike(obj: unknown): obj is Promise { - return ( - (typeof obj === 'object' || typeof obj === 'function') && - typeof (obj as any).then === 'function' - ); + return typeof obj === 'object' && typeof (obj as any).then === 'function'; } function isParserOptsFunction( From 1c251d2c43f79c64d631dc95e1baf4221370f343 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 17 Jan 2021 07:18:10 +0100 Subject: [PATCH 22/24] fix: remove redundant code --- @commitlint/load/src/utils/load-parser-opts.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index 104d3392a4..d82d196f60 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -5,7 +5,7 @@ function isObjectLike(obj: unknown): obj is Record { } function isPromiseLike(obj: unknown): obj is Promise { - return typeof obj === 'object' && typeof (obj as any).then === 'function'; + return isObjectLike(obj) && typeof (obj as any).then === 'function'; } function isParserOptsFunction( @@ -21,14 +21,11 @@ function isParserOptsFunction( export async function loadParserOpts( pendingParser: string | ParserPreset | Promise | undefined ): Promise { - if (!pendingParser || typeof pendingParser === 'string') { + if (!pendingParser || typeof pendingParser !== 'object') { return undefined; } // Await for the module, loaded with require const parser = await pendingParser; - if (typeof pendingParser !== 'object') { - return undefined; - } // Await parser opts if applicable if (isPromiseLike(parser.parserOpts)) { From f1bf2d2026e9b8485382ecb8769ac89f64bbbbe8 Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 29 Jan 2021 01:08:32 +0100 Subject: [PATCH 23/24] fix: rollback some unnecessary changes --- @commitlint/resolve-extends/src/index.test.ts | 36 +++++++++++++++++++ @commitlint/resolve-extends/src/index.ts | 4 +-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/@commitlint/resolve-extends/src/index.test.ts b/@commitlint/resolve-extends/src/index.test.ts index 98099af3dd..e651bce889 100644 --- a/@commitlint/resolve-extends/src/index.test.ts +++ b/@commitlint/resolve-extends/src/index.test.ts @@ -493,3 +493,39 @@ test('parserPreset should be merged correctly', () => { expect(actual).toEqual(expected); }); + +test('should correctly merge nested configs', () => { + const input = {extends: ['extender-1']}; + + const require = (id: string) => { + switch (id) { + case 'extender-1': + return {extends: ['extender-3', 'extender-2']}; + case 'extender-2': + return {extends: ['extender-4']}; + case 'extender-3': + return {rules: {test: 3}}; + case 'extender-4': + return {extends: ['extender-5', 'extender-6'], rules: {test: 4}}; + case 'extender-5': + return {rules: {test: 5}}; + case 'extender-6': + return {rules: {test: 6}}; + default: + return {}; + } + }; + + const ctx = {resolve: id, require: jest.fn(require)} as ResolveExtendsContext; + + const actual = resolveExtends(input, ctx); + + const expected = { + extends: ['extender-1'], + rules: { + test: 4, + }, + }; + + expect(actual).toEqual(expected); +}); diff --git a/@commitlint/resolve-extends/src/index.ts b/@commitlint/resolve-extends/src/index.ts index 597a9db556..ca91b59b05 100644 --- a/@commitlint/resolve-extends/src/index.ts +++ b/@commitlint/resolve-extends/src/index.ts @@ -55,7 +55,7 @@ function loadExtends( const {extends: e} = config; const ext = e ? (Array.isArray(e) ? e : [e]) : []; - return ext.reduceRight((configs, raw) => { + return ext.reduce((configs, raw) => { const load = context.require || require; const resolved = resolveConfig(raw, context); const c = load(resolved); @@ -81,7 +81,7 @@ function loadExtends( config.parserPreset = parserPreset; } - return [...loadExtends(c, ctx), c, ...configs]; + return [...configs, ...loadExtends(c, ctx), c]; }, []); } From 2c323146a240ee25d8f2a5e234ae6e25a6ce4c12 Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 29 Jan 2021 05:36:14 +0100 Subject: [PATCH 24/24] fix: optimize loadParserOpts --- .../load/src/utils/load-parser-opts.ts | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/@commitlint/load/src/utils/load-parser-opts.ts b/@commitlint/load/src/utils/load-parser-opts.ts index d82d196f60..9d8c2d9390 100644 --- a/@commitlint/load/src/utils/load-parser-opts.ts +++ b/@commitlint/load/src/utils/load-parser-opts.ts @@ -4,16 +4,10 @@ function isObjectLike(obj: unknown): obj is Record { return Boolean(obj) && typeof obj === 'object'; // typeof null === 'object' } -function isPromiseLike(obj: unknown): obj is Promise { - return isObjectLike(obj) && typeof (obj as any).then === 'function'; -} - function isParserOptsFunction( obj: T ): obj is T & { - parserOpts: ( - cb: (_: never, parserOpts: Record) => unknown - ) => Record | undefined; + parserOpts: (...args: any[]) => any; } { return typeof obj.parserOpts === 'function'; } @@ -27,9 +21,21 @@ export async function loadParserOpts( // Await for the module, loaded with require const parser = await pendingParser; - // Await parser opts if applicable - if (isPromiseLike(parser.parserOpts)) { - parser.parserOpts = ((await parser.parserOpts) as any).parserOpts; + // exit early, no opts to resolve + if (!parser.parserOpts) { + return parser; + } + + // Pull nested parserOpts, might happen if overwritten with a module in main config + if (typeof parser.parserOpts === 'object') { + // Await parser opts if applicable + parser.parserOpts = await parser.parserOpts; + if ( + isObjectLike(parser.parserOpts) && + isObjectLike(parser.parserOpts.parserOpts) + ) { + parser.parserOpts = parser.parserOpts.parserOpts; + } return parser; } @@ -40,10 +46,10 @@ export async function loadParserOpts( parser.name.startsWith('conventional-changelog-') ) { return new Promise((resolve) => { - const result = parser.parserOpts((_: never, opts) => { + const result = parser.parserOpts((_: never, opts: any) => { resolve({ ...parser, - parserOpts: opts.parserOpts, + parserOpts: opts?.parserOpts, }); }); @@ -53,7 +59,7 @@ export async function loadParserOpts( Promise.resolve(result).then((opts) => { resolve({ ...parser, - parserOpts: opts.parserOpts, + parserOpts: opts?.parserOpts, }); }); } @@ -61,12 +67,5 @@ export async function loadParserOpts( }); } - // Pull nested parserOpts, might happen if overwritten with a module in main config - if ( - isObjectLike(parser.parserOpts) && - typeof parser.parserOpts.parserOpts === 'object' - ) { - parser.parserOpts = parser.parserOpts.parserOpts; - } return parser; }