Skip to content

Commit

Permalink
Merge branch 'master' into toolkit
Browse files Browse the repository at this point in the history
  • Loading branch information
illright committed Oct 12, 2024
2 parents 62f938a + 281aaca commit 1014bce
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/olive-planets-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'steiger': minor
---

Add error messages for invalid config shapes
6 changes: 2 additions & 4 deletions packages/steiger/src/models/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Config, GlobalIgnore, Plugin, Rule } from '@steiger/types'

import createRuleInstructions from './create-rule-instructions'
import { RuleInstructions } from './types'
import buildValidationScheme from './build-validation-scheme'
import { validateConfig } from './validate-config'
import { isGlobalIgnore, isPlugin } from './raw-config'
import { transformGlobs } from './transform-globs'

Expand Down Expand Up @@ -32,9 +32,7 @@ export const $enabledRules = combine($ruleInstructions, $plugins, (ruleInstructi
})

export function processConfiguration(rawConfig: Config<Array<Rule>>, configLocationFolder: string | null) {
const validationScheme = buildValidationScheme(rawConfig)
const validatedConfig = validationScheme.parse(rawConfig)

const validatedConfig = validateConfig(rawConfig)
const plugins = rawConfig.filter(isPlugin)
const configTransformedGlobs = transformGlobs(validatedConfig, configLocationFolder)
const ruleInstructions = createRuleInstructions(configTransformedGlobs)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest'
import { Config, Plugin, Rule } from '@steiger/types'

import buildValidationScheme from './build-validation-scheme'
import { buildValidationScheme, validateConfig } from './validate-config'
import { isPlugin } from './raw-config'

// The function maps a plugin object to a new object with the same properties as the original object,
Expand Down Expand Up @@ -313,3 +313,21 @@ describe('buildValidationScheme', () => {
expect(() => scheme.parse(config)).toThrow()
})
})

describe('validateConfig', () => {
it('should throw an error when an old-style config is provided', () => {
// @ts-expect-error testing invalid input
expect(() => validateConfig({})).toThrow()
})

it('should throw an error when a config of wrong shape is provided ', () => {
// @ts-expect-error testing invalid input
expect(() => validateConfig('')).toThrow()

// @ts-expect-error testing invalid input
expect(() => validateConfig(234234234)).toThrow()

// @ts-expect-error testing invalid input
expect(() => validateConfig(true)).toThrow()
})
})
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import z from 'zod'

import { BaseRuleOptions, Config, Plugin, Rule } from '@steiger/types'

import { getOptions, isConfigObject, isPlugin } from './raw-config'
import { isEqual } from '../../shared/objects'
import { BaseRuleOptions, Config, Plugin, Rule } from '@steiger/types'

const OLD_CONFIG_ERROR_MESSAGE =
'Old configuration format detected. We are evolving!\nPlease follow this short guide to migrate to the new one:\nhttps://github.com/feature-sliced/steiger/blob/master/MIGRATION_GUIDE.md'
const WRONG_CONFIG_SHAPE_ERROR_MESSAGE =
'The config should be an Array, but the provided config is not.\nHere is a link to the documentation that might help to fix it:\nhttps://github.com/feature-sliced/steiger?tab=readme-ov-file#configuration'
const NO_RULES_ERROR_MESSAGE = 'At least one rule must be provided by plugins!'
const NO_CONFIG_OBJECTS_ERROR_MESSAGE = 'At least one config object must be provided!'

function getAllRuleNames(plugins: Array<Plugin>) {
const allRules = plugins.flatMap((plugin) => plugin.ruleDefinitions)
Expand All @@ -15,7 +23,7 @@ function validateConfigObjectsNumber(value: Config<Array<Rule>>, ctx: z.Refineme
if (configObjects.length === 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'At least one config object must be provided!',
message: NO_CONFIG_OBJECTS_ERROR_MESSAGE,
})
}
}
Expand Down Expand Up @@ -67,13 +75,13 @@ function validateRuleOptions(value: Config<Array<Rule>>, ctx: z.RefinementCtx) {
/**
* Dynamically build a validation scheme based on the rules provided by plugins.
* */
export default function buildValidationScheme(rawConfig: Config<Array<Rule>>) {
export function buildValidationScheme(rawConfig: Config<Array<Rule>>) {
const allRuleNames = getAllRuleNames(rawConfig.filter(isPlugin))

// Make sure there's at least one rule registered by plugins
// Need to check this before creating the scheme, because zod.enum requires at least one element
if (allRuleNames.length === 0) {
throw new Error('At least one rule must be provided by plugins!')
throw new Error(NO_RULES_ERROR_MESSAGE)
}

// Marked as "any" because return type is not useful for this validation
Expand Down Expand Up @@ -122,3 +130,21 @@ export default function buildValidationScheme(rawConfig: Config<Array<Rule>>) {
.superRefine(validateRuleOptions)
.superRefine(validateRuleUniqueness)
}

export function validateConfig(rawConfig: Config<Array<Rule>>) {
const isOldConfig = typeof rawConfig === 'object' && !Array.isArray(rawConfig)
const isWrongShape = !Array.isArray(rawConfig)

// Need to check the shape of the config separately before validating it,
// because building a validation scheme requires the config to be an array
if (isOldConfig) {
throw new Error(OLD_CONFIG_ERROR_MESSAGE)
}

if (isWrongShape) {
throw new Error(WRONG_CONFIG_SHAPE_ERROR_MESSAGE)
}

const validationScheme = buildValidationScheme(rawConfig)
return validationScheme.parse(rawConfig)
}

0 comments on commit 1014bce

Please sign in to comment.