From d908ca3c2e825774a7fc2e05ed858cd951ae6386 Mon Sep 17 00:00:00 2001
From: RahulGautamSingh <rahultesnik@gmail.com>
Date: Mon, 22 Jan 2024 14:08:59 +0545
Subject: [PATCH] refactor: config option parent -> parents (#26609)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 .../__snapshots__/validation.spec.ts.snap     |   2 +-
 lib/config/defaults.ts                        |   2 +-
 lib/config/options/index.ts                   | 130 +++++++++---------
 lib/config/types.ts                           |  13 +-
 lib/config/validation.ts                      |  13 +-
 test/documentation.spec.ts                    |   4 +-
 tools/docs/schema.ts                          |  38 ++---
 7 files changed, 105 insertions(+), 97 deletions(-)

diff --git a/lib/config/__snapshots__/validation.spec.ts.snap b/lib/config/__snapshots__/validation.spec.ts.snap
index 14aef329c7..c2c5c75f5c 100644
--- a/lib/config/__snapshots__/validation.spec.ts.snap
+++ b/lib/config/__snapshots__/validation.spec.ts.snap
@@ -259,7 +259,7 @@ exports[`config/validation validateConfig(config) validates regEx for each fileM
 exports[`config/validation validateConfig(config) warns if hostType has the wrong parent 1`] = `
 [
   {
-    "message": "hostType should only be configured within a "hostRules" object. Was found in .",
+    "message": "hostType should only be configured within one of "hostRules" objects. Was found in .",
     "topic": "hostType",
   },
 ]
diff --git a/lib/config/defaults.ts b/lib/config/defaults.ts
index 43c18286bf..1fd65e07b6 100644
--- a/lib/config/defaults.ts
+++ b/lib/config/defaults.ts
@@ -23,7 +23,7 @@ export function getConfig(): AllConfig {
   const options = getOptions();
   const config: AllConfig = {};
   options.forEach((option) => {
-    if (!option.parent) {
+    if (!option.parents) {
       config[option.name] = getDefault(option);
     }
   });
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index e5896456e7..17a556a8c6 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -72,7 +72,7 @@ const options: RenovateOptions[] = [
       'A list of post-upgrade commands that are executed before a commit is made by Renovate.',
     type: 'array',
     subType: 'string',
-    parent: 'postUpgradeTasks',
+    parents: ['postUpgradeTasks'],
     default: [],
     cli: false,
   },
@@ -82,7 +82,7 @@ const options: RenovateOptions[] = [
       'Files that match the glob pattern will be committed after running a post-upgrade task.',
     type: 'array',
     subType: 'string',
-    parent: 'postUpgradeTasks',
+    parents: ['postUpgradeTasks'],
     default: ['**/*'],
     cli: false,
   },
@@ -90,7 +90,7 @@ const options: RenovateOptions[] = [
     name: 'format',
     description: 'Format of the custom datasource.',
     type: 'string',
-    parent: 'customDatasources',
+    parents: ['customDatasources'],
     default: 'json',
     allowedValues: ['json', 'plain'],
     cli: false,
@@ -101,7 +101,7 @@ const options: RenovateOptions[] = [
     description:
       'Controls when the post upgrade tasks run: on every update, or once per upgrade branch.',
     type: 'string',
-    parent: 'postUpgradeTasks',
+    parents: ['postUpgradeTasks'],
     allowedValues: ['update', 'branch'],
     default: 'update',
     cli: false,
@@ -1012,7 +1012,7 @@ const options: RenovateOptions[] = [
       'Template for generating a `defaultRegistryUrl` for custom datasource.',
     type: 'string',
     default: '',
-    parent: 'customDatasources',
+    parents: ['customDatasources'],
     cli: false,
     env: false,
   },
@@ -1099,7 +1099,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     allowString: true,
-    parent: 'packageRules',
+    parents: ['packageRules'],
     stage: 'package',
     mergeable: true,
     cli: false,
@@ -1113,7 +1113,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1126,7 +1126,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1138,7 +1138,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     allowString: true,
-    parent: 'packageRules',
+    parents: ['packageRules'],
     stage: 'package',
     mergeable: true,
     cli: false,
@@ -1151,7 +1151,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     allowString: true,
-    parent: 'packageRules',
+    parents: ['packageRules'],
     stage: 'package',
     mergeable: true,
     cli: false,
@@ -1164,7 +1164,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     allowString: true,
-    parent: 'packageRules',
+    parents: ['packageRules'],
     stage: 'package',
     mergeable: true,
     cli: false,
@@ -1177,7 +1177,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     allowString: true,
-    parent: 'packageRules',
+    parents: ['packageRules'],
     stage: 'package',
     mergeable: true,
     cli: false,
@@ -1191,7 +1191,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1204,7 +1204,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1217,7 +1217,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1231,7 +1231,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1245,7 +1245,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1258,7 +1258,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1272,7 +1272,7 @@ const options: RenovateOptions[] = [
     format: 'regex',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1286,7 +1286,7 @@ const options: RenovateOptions[] = [
     format: 'regex',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1300,7 +1300,7 @@ const options: RenovateOptions[] = [
     format: 'regex',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1315,7 +1315,7 @@ const options: RenovateOptions[] = [
     format: 'regex',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1327,7 +1327,7 @@ const options: RenovateOptions[] = [
       'A regex to match against the raw `currentValue` string of a dependency. Valid only within a `packageRules` object.',
     type: 'string',
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1338,7 +1338,7 @@ const options: RenovateOptions[] = [
       'A version, or range of versions, to match against the current version of a package. Valid only within a `packageRules` object.',
     type: 'string',
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1351,7 +1351,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1363,7 +1363,7 @@ const options: RenovateOptions[] = [
     subType: 'string',
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1381,7 +1381,7 @@ const options: RenovateOptions[] = [
       'The name of the new dependency that replaces the old deprecated dependency.',
     type: 'string',
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     cli: false,
     env: false,
   },
@@ -1391,7 +1391,7 @@ const options: RenovateOptions[] = [
     type: 'string',
     default: '{{{packageName}}}',
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     cli: false,
     env: false,
   },
@@ -1401,7 +1401,7 @@ const options: RenovateOptions[] = [
       'The version of the new dependency that replaces the old deprecated dependency.',
     type: 'string',
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     cli: false,
     env: false,
   },
@@ -1414,7 +1414,7 @@ const options: RenovateOptions[] = [
     allowedValues: ['low', 'neutral', 'high', 'very high'],
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1440,7 +1440,7 @@ const options: RenovateOptions[] = [
     ],
     allowString: true,
     stage: 'package',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     mergeable: true,
     cli: false,
     env: false,
@@ -1452,7 +1452,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     stage: 'repository',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     cli: false,
     env: false,
   },
@@ -1462,7 +1462,7 @@ const options: RenovateOptions[] = [
     description:
       'A version range or regex pattern capturing allowed versions for dependencies.',
     type: 'string',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     stage: 'package',
     cli: false,
     env: false,
@@ -1473,7 +1473,7 @@ const options: RenovateOptions[] = [
       'If set, Renovate will use this URL to fetch changelogs for a matched dependency. Valid only within a `packageRules` object.',
     type: 'string',
     stage: 'pr',
-    parent: 'packageRules',
+    parents: ['packageRules'],
     cli: false,
     env: false,
   },
@@ -1872,7 +1872,7 @@ const options: RenovateOptions[] = [
     description: 'List of jsonata transformation rules.',
     type: 'array',
     subType: 'string',
-    parent: 'customDatasources',
+    parents: ['customDatasources'],
     default: [],
   },
   {
@@ -2286,7 +2286,7 @@ const options: RenovateOptions[] = [
       'hostType for a package rule. Can be a platform name or a datasource name.',
     type: 'string',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     cli: false,
     env: false,
   },
@@ -2295,7 +2295,7 @@ const options: RenovateOptions[] = [
     description: 'A domain name, host name or base URL to match against.',
     type: 'string',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     cli: false,
     env: false,
   },
@@ -2304,7 +2304,7 @@ const options: RenovateOptions[] = [
     description: 'Timeout (in milliseconds) for queries to external endpoints.',
     type: 'integer',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     cli: false,
     env: false,
   },
@@ -2313,7 +2313,7 @@ const options: RenovateOptions[] = [
     description: 'Explicitly turn on insecure Docker registry access (HTTP).',
     type: 'boolean',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     cli: false,
     env: false,
     advancedUse: true,
@@ -2324,7 +2324,7 @@ const options: RenovateOptions[] = [
       'If enabled, Renovate aborts its run when HTTP request errors occur.',
     type: 'boolean',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: false,
     cli: false,
     env: false,
@@ -2336,7 +2336,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'number',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     cli: false,
     env: false,
   },
@@ -2345,7 +2345,7 @@ const options: RenovateOptions[] = [
     description: 'Enable got HTTP/2 support.',
     type: 'boolean',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: false,
     cli: false,
     env: false,
@@ -2355,7 +2355,7 @@ const options: RenovateOptions[] = [
     description: 'Limit concurrent requests per host.',
     type: 'integer',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: null,
     cli: false,
     env: false,
@@ -2365,7 +2365,7 @@ const options: RenovateOptions[] = [
     description: 'Limit requests rate per host.',
     type: 'integer',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: 0,
     cli: false,
     env: false,
@@ -2376,7 +2376,7 @@ const options: RenovateOptions[] = [
       'Authentication type for HTTP header. e.g. `"Bearer"` or `"Basic"`. Use `"Token-Only"` to use only the token without an authorization type.',
     type: 'string',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: 'Bearer',
     cli: false,
     env: false,
@@ -2386,7 +2386,7 @@ const options: RenovateOptions[] = [
     description: 'Enable got DNS cache.',
     type: 'boolean',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: false,
     cli: false,
     env: false,
@@ -2397,7 +2397,7 @@ const options: RenovateOptions[] = [
     description: 'Enable HTTP keep-alive for hosts.',
     type: 'boolean',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: false,
     cli: false,
     env: false,
@@ -2408,7 +2408,7 @@ const options: RenovateOptions[] = [
     description:
       'Put fields to be forwarded to the HTTP request headers in the headers config option.',
     type: 'object',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     cli: false,
     env: false,
     advancedUse: true,
@@ -2421,7 +2421,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     allowedValues: ['composer'],
     default: null,
     cli: false,
@@ -2432,7 +2432,7 @@ const options: RenovateOptions[] = [
     description: 'The overriding trusted CA certificate.',
     type: 'string',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: null,
     cli: false,
     env: false,
@@ -2442,7 +2442,7 @@ const options: RenovateOptions[] = [
     description: 'The private key in PEM format.',
     type: 'string',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: null,
     cli: false,
     env: false,
@@ -2452,7 +2452,7 @@ const options: RenovateOptions[] = [
     description: 'The certificate chains in PEM format.',
     type: 'string',
     stage: 'repository',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     default: null,
     cli: false,
     env: false,
@@ -2576,7 +2576,7 @@ const options: RenovateOptions[] = [
       'Custom manager to use. Valid only within a `customManagers` object.',
     type: 'string',
     allowedValues: ['regex'],
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2587,7 +2587,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     subType: 'string',
     format: 'regex',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2597,7 +2597,7 @@ const options: RenovateOptions[] = [
     type: 'string',
     default: 'any',
     allowedValues: ['any', 'recursive', 'combination'],
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2606,7 +2606,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional depName for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2615,7 +2615,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional packageName for extracted dependencies, else defaults to `depName` value. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2624,7 +2624,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional datasource for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2633,7 +2633,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional `depType` for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2642,7 +2642,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional `currentValue` for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2651,7 +2651,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional versioning for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2660,7 +2660,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional registry URL for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2669,7 +2669,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional `extractVersion` for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2678,7 +2678,7 @@ const options: RenovateOptions[] = [
     description:
       'Optional `extractVersion` for extracted dependencies. Valid only within a `customManagers` object.',
     type: 'string',
-    parent: 'customManagers',
+    parents: ['customManagers'],
     cli: false,
     env: false,
   },
@@ -2795,7 +2795,7 @@ const options: RenovateOptions[] = [
     type: 'integer',
     default: 60,
     stage: 'package',
-    parent: 'hostRules',
+    parents: ['hostRules'],
     cli: false,
     env: false,
   },
diff --git a/lib/config/types.ts b/lib/config/types.ts
index dddb598cc4..6036837478 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -368,6 +368,12 @@ export interface ValidationMessage {
   message: string;
 }
 
+export type AllowedParents =
+  | 'customManagers'
+  | 'customDatasources'
+  | 'hostRules'
+  | 'postUpgradeTasks'
+  | 'packageRules';
 export interface RenovateOptionBase {
   /**
    * If true, the option can only be configured by people with access to the Renovate instance.
@@ -396,12 +402,7 @@ export interface RenovateOptionBase {
 
   name: string;
 
-  parent?:
-    | 'customDatasources'
-    | 'hostRules'
-    | 'packageRules'
-    | 'postUpgradeTasks'
-    | 'customManagers';
+  parents?: AllowedParents[];
 
   stage?: RenovateConfigStage;
 
diff --git a/lib/config/validation.ts b/lib/config/validation.ts
index b22bddaebc..0a459f8c37 100644
--- a/lib/config/validation.ts
+++ b/lib/config/validation.ts
@@ -20,6 +20,7 @@ import { migrateConfig } from './migration';
 import { getOptions } from './options';
 import { resolveConfigPresets } from './presets';
 import {
+  AllowedParents,
   type RenovateConfig,
   type RenovateOptions,
   type StatusCheckKey,
@@ -32,7 +33,7 @@ import * as managerValidator from './validation-helpers/managers';
 const options = getOptions();
 
 let optionTypes: Record<string, RenovateOptions['type']>;
-let optionParents: Record<string, RenovateOptions['parent']>;
+let optionParents: Record<string, AllowedParents[]>;
 let optionGlobals: Set<string>;
 
 const managerList = getManagerList();
@@ -117,8 +118,8 @@ export async function validateConfig(
   if (!optionParents) {
     optionParents = {};
     options.forEach((option) => {
-      if (option.parent) {
-        optionParents[option.name] = option.parent;
+      if (option.parents) {
+        optionParents[option.name] = option.parents;
       }
     });
   }
@@ -224,10 +225,12 @@ export async function validateConfig(
       if (
         !isPreset &&
         optionParents[key] &&
-        optionParents[key] !== parentName
+        !optionParents[key].includes(parentName as AllowedParents)
       ) {
         // TODO: types (#22198)
-        const message = `${key} should only be configured within a "${optionParents[key]}" object. Was found in ${parentName}`;
+        const message = `${key} should only be configured within one of "${optionParents[
+          key
+        ]?.join(' or ')}" objects. Was found in ${parentName}`;
         warnings.push({
           topic: `${parentPath ? `${parentPath}.` : ''}${key}`,
           message,
diff --git a/test/documentation.spec.ts b/test/documentation.spec.ts
index d40319fbf0..f0f66b1e94 100644
--- a/test/documentation.spec.ts
+++ b/test/documentation.spec.ts
@@ -31,7 +31,7 @@ describe('documentation', () => {
 
       const expectedOptions = options
         .filter((option) => !option.globalOnly)
-        .filter((option) => !option.parent)
+        .filter((option) => !option.parents)
         .filter((option) => !option.autogenerated)
         .map((option) => option.name)
         .sort();
@@ -51,7 +51,7 @@ describe('documentation', () => {
       const expectedSubOptions = options
         .filter((option) => option.stage !== 'global')
         .filter((option) => !option.globalOnly)
-        .filter((option) => option.parent)
+        .filter((option) => option.parents)
         .map((option) => option.name)
         .sort();
       expectedSubOptions.sort();
diff --git a/tools/docs/schema.ts b/tools/docs/schema.ts
index 456b3b00aa..f02b13632e 100644
--- a/tools/docs/schema.ts
+++ b/tools/docs/schema.ts
@@ -75,7 +75,7 @@ function createSingleConfig(option: RenovateOptions): Record<string, unknown> {
 
 function createSchemaForParentConfigs(): void {
   for (const option of options) {
-    if (!option.parent) {
+    if (!option.parents) {
       properties[option.name] = createSingleConfig(option);
     }
   }
@@ -83,30 +83,34 @@ function createSchemaForParentConfigs(): void {
 
 function addChildrenArrayInParents(): void {
   for (const option of options) {
-    if (option.parent) {
-      properties[option.parent].items = {
-        allOf: [
-          {
-            type: 'object',
-            properties: {
-              description: {
-                type: 'string',
-                description:
-                  'A custom description for this configuration object',
+    if (option.parents) {
+      for (const parent of option.parents) {
+        properties[parent].items = {
+          allOf: [
+            {
+              type: 'object',
+              properties: {
+                description: {
+                  type: 'string',
+                  description:
+                    'A custom description for this configuration object',
+                },
               },
             },
-          },
-        ],
-      };
+          ],
+        };
+      }
     }
   }
 }
 
 function createSchemaForChildConfigs(): void {
   for (const option of options) {
-    if (option.parent) {
-      properties[option.parent].items.allOf[0].properties[option.name] =
-        createSingleConfig(option);
+    if (option.parents) {
+      for (const parent of option.parents) {
+        properties[parent].items.allOf[0].properties[option.name] =
+          createSingleConfig(option);
+      }
     }
   }
 }
-- 
GitLab