1 |
|
2 |
|
3 | import debug from 'debug'
|
4 | import inspect from 'object-inspect'
|
5 |
|
6 | import { configurationError } from './messages.js'
|
7 | import { ConfigEmptyError, ConfigFormatError } from './symbols.js'
|
8 | import { validateBraces } from './validateBraces.js'
|
9 |
|
10 | const debugLog = debug('lint-staged:validateConfig')
|
11 |
|
12 | const isObject = (test) => test && typeof test === 'object' && !Array.isArray(test)
|
13 |
|
14 | const TEST_DEPRECATED_KEYS = new Map([
|
15 | ['concurrent', (key) => typeof key === 'boolean'],
|
16 | ['chunkSize', (key) => typeof key === 'number'],
|
17 | ['globOptions', isObject],
|
18 | ['linters', isObject],
|
19 | ['ignore', (key) => Array.isArray(key)],
|
20 | ['subTaskConcurrency', (key) => typeof key === 'number'],
|
21 | ['renderer', (key) => typeof key === 'string'],
|
22 | ['relative', (key) => typeof key === 'boolean'],
|
23 | ])
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | export const validateConfig = (config, configPath, logger) => {
|
33 | debugLog('Validating config from `%s`...', configPath)
|
34 |
|
35 | if (!config || (typeof config !== 'object' && typeof config !== 'function')) {
|
36 | throw ConfigFormatError
|
37 | }
|
38 |
|
39 | |
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | if (typeof config === 'function') {
|
47 | return { '*': config }
|
48 | }
|
49 |
|
50 | if (Object.entries(config).length === 0) {
|
51 | throw ConfigEmptyError
|
52 | }
|
53 |
|
54 | const errors = []
|
55 |
|
56 | |
57 |
|
58 |
|
59 |
|
60 |
|
61 | const validatedConfig = Object.entries(config).reduce((collection, [pattern, task]) => {
|
62 |
|
63 | if (TEST_DEPRECATED_KEYS.has(pattern)) {
|
64 | const testFn = TEST_DEPRECATED_KEYS.get(pattern)
|
65 | if (testFn(task)) {
|
66 | errors.push(
|
67 | configurationError(pattern, 'Advanced configuration has been deprecated.', task)
|
68 | )
|
69 | }
|
70 |
|
71 |
|
72 | return collection
|
73 | }
|
74 |
|
75 | if (
|
76 | (!Array.isArray(task) ||
|
77 | task.some((item) => typeof item !== 'string' && typeof item !== 'function')) &&
|
78 | typeof task !== 'string' &&
|
79 | typeof task !== 'function'
|
80 | ) {
|
81 | errors.push(
|
82 | configurationError(
|
83 | pattern,
|
84 | 'Should be a string, a function, or an array of strings and functions.',
|
85 | task
|
86 | )
|
87 | )
|
88 | }
|
89 |
|
90 | |
91 |
|
92 |
|
93 |
|
94 | const fixedPattern = validateBraces(pattern, logger)
|
95 |
|
96 | return { ...collection, [fixedPattern]: task }
|
97 | }, {})
|
98 |
|
99 | if (errors.length) {
|
100 | const message = errors.join('\n\n')
|
101 |
|
102 | logger.error(`Could not parse lint-staged config.
|
103 |
|
104 | ${message}
|
105 |
|
106 | See https://github.com/okonet/lint-staged#configuration.`)
|
107 |
|
108 | throw new Error(message)
|
109 | }
|
110 |
|
111 | debugLog('Validated config from `%s`:', configPath)
|
112 | debugLog(inspect(config, { indent: 2 }))
|
113 |
|
114 | return validatedConfig
|
115 | }
|