Skip to content

Commit

Permalink
Use functors for config selection
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Jan 16, 2022
1 parent 007af75 commit 2882e7f
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 37 deletions.
52 changes: 16 additions & 36 deletions src/config/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,44 +23,20 @@ import {
checkJson,
} from './check.js'
import { normalizeConfigPath, normalizeConfigGlob } from './path.js'
import { validateConfigSelector, isConfigSelector } from './select/normalize.js'
import { cRecurseConfigSelectors } from './select/normalize.js'

// Normalize configuration shape and do custom validation
export const normalizeConfig = function (config, configInfos) {
// eslint-disable-next-line fp/no-mutating-methods
const configInfosA = [...configInfos].reverse()
return mapObj(config, (propName, value) => [
propName,
normalizePropDeep(value, propName, configInfosA),
return mapObj(config, (name, value) => [
name,
normalizePropValue(value, name, configInfosA),
])
}

// If a configuration property uses selectors or variations, normalization must
// be applied recursively.
const normalizePropDeep = function (value, propName, configInfos) {
if (!isDeepProp(value, propName)) {
return normalizePropValue({ value, propName, name: propName, configInfos })
}

validateConfigSelector(value, propName)

return mapObj(value, (selector, childValue) => [
selector,
normalizePropValue({
value: childValue,
propName,
name: `${propName}.${selector}`,
configInfos,
}),
])
}

const isDeepProp = function (configValue, propName) {
return isConfigSelector(configValue, propName)
}

const normalizePropValue = function ({ value, propName, name, configInfos }) {
const normalizer = NORMALIZERS[propName]
const normalizePropValue = function (value, name, configInfos) {
const normalizer = NORMALIZERS[name]
return normalizer === undefined
? value
: runNormalizer(normalizer, value, name, configInfos)
Expand All @@ -73,14 +49,18 @@ const NORMALIZERS = {
delta: normalizeDelta,
force: checkBoolean,
inputs: cCheckObjectProps([checkJson]),
limit: composeNormalizers(checkInteger, normalizeLimit),
limit: cRecurseConfigSelectors(
composeNormalizers(checkInteger, normalizeLimit),
),
merge: composeNormalizers(checkDefinedString, validateMerge),
output: composeNormalizers(
checkDefinedString,
condition(normalizeConfigPath, isOutputPath),
),
outliers: checkBoolean,
precision: composeNormalizers(checkInteger, normalizePrecision),
outliers: cRecurseConfigSelectors(checkBoolean),
precision: cRecurseConfigSelectors(
composeNormalizers(checkInteger, normalizePrecision),
),
quiet: checkBoolean,
reporter: composeNormalizers(
normalizeOptionalArray,
Expand All @@ -96,11 +76,11 @@ const NORMALIZERS = {
normalizeOptionalArray,
cCheckArrayItems([checkString]),
),
showDiff: checkBoolean,
showDiff: cRecurseConfigSelectors(checkBoolean),
showMetadata: checkBoolean,
showPrecision: checkBoolean,
showPrecision: cRecurseConfigSelectors(checkBoolean),
showSystem: checkBoolean,
showTitles: checkBoolean,
showTitles: cRecurseConfigSelectors(checkBoolean),
since: normalizeDelta,
system: cCheckObjectProps([checkDefinedString]),
tasks: composeNormalizers(
Expand Down
26 changes: 25 additions & 1 deletion src/config/select/normalize.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
import isPlainObj from 'is-plain-obj'
import mapObj from 'map-obj'

import { UserError } from '../../error/main.js'
import { curry } from '../../utils/functional.js'

// If a configuration property uses selectors, normalization must be applied
// recursively.
// eslint-disable-next-line max-params
const recurseConfigSelectors = function (callFunc, value, name, ...args) {
if (!isConfigSelectorShape(value)) {
return callFunc(value, name, ...args)
}

validateConfigSelector(value, name)

return mapObj(value, (selector, childValue) => [
selector,
callFunc(childValue, `${name}.${selector}`, ...args),
])
}

export const cRecurseConfigSelectors = curry(recurseConfigSelectors)

// We validate that at least one selector is named "default"
// - This ensures users understand that this selector is used as a fallback
Expand Down Expand Up @@ -30,7 +50,11 @@ export const validateConfigSelector = function (configValue, propName) {

// Check if a configuration property uses selectors
export const isConfigSelector = function (configValue, propName) {
return SELECTABLE_PROPS.has(propName) && isPlainObj(configValue)
return SELECTABLE_PROPS.has(propName) && isConfigSelectorShape(configValue)
}

const isConfigSelectorShape = function (configValue) {
return isPlainObj(configValue)
}

// List of properties which can use configuration selectors
Expand Down

0 comments on commit 2882e7f

Please sign in to comment.