Skip to content

Commit 32164ba

Browse files
committed
normalize the configuration
1 parent 4a69480 commit 32164ba

File tree

6 files changed

+202
-78
lines changed

6 files changed

+202
-78
lines changed

src/lib/expandTailwindAtRules.js

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,6 @@ const builtInTransformers = {
3535

3636
function getExtractor(tailwindConfig, fileExtension) {
3737
let extractors = tailwindConfig.content.extract
38-
let contentOptions = tailwindConfig.content.options
39-
40-
if (typeof extractors === 'function') {
41-
extractors = {
42-
DEFAULT: extractors,
43-
}
44-
}
45-
if (contentOptions.defaultExtractor) {
46-
extractors.DEFAULT = contentOptions.defaultExtractor
47-
}
48-
for (let { extensions, extractor } of contentOptions.extractors || []) {
49-
for (let extension of extensions) {
50-
extractors[extension] = extractor
51-
}
52-
}
5338

5439
return (
5540
extractors[fileExtension] ||
@@ -62,12 +47,6 @@ function getExtractor(tailwindConfig, fileExtension) {
6247
function getTransformer(tailwindConfig, fileExtension) {
6348
let transformers = tailwindConfig.content.transform
6449

65-
if (typeof transformers === 'function') {
66-
transformers = {
67-
DEFAULT: transformers,
68-
}
69-
}
70-
7150
return (
7251
transformers[fileExtension] ||
7352
transformers.DEFAULT ||

src/lib/setupTrackingContext.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function getCandidateFiles(context, tailwindConfig) {
2626
return candidateFilesCache.get(context)
2727
}
2828

29-
let candidateFiles = tailwindConfig.content.content
29+
let candidateFiles = tailwindConfig.content.files
3030
.filter((item) => typeof item === 'string')
3131
.map((contentPath) => normalizePath(contentPath))
3232

@@ -77,7 +77,7 @@ function getTailwindConfig(configOrPath) {
7777
}
7878

7979
function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
80-
let changedContent = context.tailwindConfig.content.content
80+
let changedContent = context.tailwindConfig.content.files
8181
.filter((item) => typeof item.raw === 'string')
8282
.map(({ raw, extension }) => ({ content: raw, extension }))
8383

src/lib/setupWatchingContext.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ function getCandidateFiles(context, tailwindConfig) {
147147
return candidateFilesCache.get(context)
148148
}
149149

150-
let candidateFiles = tailwindConfig.content.content
150+
let candidateFiles = tailwindConfig.content.files
151151
.filter((item) => typeof item === 'string')
152152
.map((contentPath) => normalizePath(contentPath))
153153

@@ -185,7 +185,7 @@ function getTailwindConfig(configOrPath) {
185185
}
186186

187187
function resolvedChangedContent(context, candidateFiles) {
188-
let changedContent = context.tailwindConfig.content.content
188+
let changedContent = context.tailwindConfig.content.files
189189
.filter((item) => typeof item.raw === 'string')
190190
.map(({ raw, extension }) => ({ content: raw, extension }))
191191

src/util/normalizeConfig.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import log from './log'
2+
3+
function validate(config, options) {
4+
if (!config) return false
5+
6+
for (let [k, v] of Object.entries(config)) {
7+
let types = options[k]
8+
9+
if (!types) return false
10+
11+
// Property SHOULD exist, this catches unused keys like `options`
12+
if (!types.includes(undefined) && !options.hasOwnProperty(k)) {
13+
return false
14+
}
15+
16+
if (
17+
!types.some((type) => {
18+
if (type === undefined) return true
19+
return v instanceof type
20+
})
21+
) {
22+
return false
23+
}
24+
}
25+
26+
for (let [k, types] of Object.entries(options)) {
27+
let value = config[k]
28+
if (
29+
!types.some((type) => {
30+
if (type === undefined) return true
31+
return value instanceof type
32+
})
33+
) {
34+
return false
35+
}
36+
}
37+
38+
return true
39+
}
40+
41+
export function normalizeConfig(config) {
42+
// Quick structure validation
43+
let valid = validate(config.content, {
44+
files: [Array],
45+
extract: [undefined, Function, Object],
46+
})
47+
48+
if (!valid) {
49+
log.warn('purge-deprecation', [
50+
'The `purge` option in your tailwind.config.js file has been deprecated.',
51+
'Please rename this to `content` instead.',
52+
])
53+
}
54+
55+
// Normalize the `safelist`
56+
config.safelist = (() => {
57+
let { content, purge, safelist } = config
58+
59+
if (Array.isArray(safelist)) return safelist
60+
if (Array.isArray(content?.safelist)) return content.safelist
61+
if (Array.isArray(purge?.safelist)) return purge.safelist
62+
if (Array.isArray(purge?.options?.safelist)) return purge.options.safelist
63+
64+
return []
65+
})()
66+
67+
// Normalize the `content`
68+
config.content = {
69+
files: (() => {
70+
let { content, purge } = config
71+
72+
if (Array.isArray(purge)) return purge
73+
if (Array.isArray(purge?.content)) return purge.content
74+
if (Array.isArray(content)) return content
75+
if (Array.isArray(content?.content)) return content.content
76+
if (Array.isArray(content?.files)) return content.files
77+
78+
return []
79+
})(),
80+
81+
extract: (() => {
82+
let extract = (() => {
83+
if (config.purge?.extract) return config.purge.extract
84+
if (config.content?.extract) return config.content.extract
85+
86+
if (config.purge?.extract?.DEFAULT) return config.purge.extract.DEFAULT
87+
if (config.content?.extract?.DEFAULT) return config.content.extract.DEFAULT
88+
89+
if (config.purge?.options?.defaultExtractor) return config.purge.options.defaultExtractor
90+
if (config.content?.options?.defaultExtractor)
91+
return config.content.options.defaultExtractor
92+
93+
if (config.purge?.options?.extractors) return config.purge.options.extractors
94+
if (config.content?.options?.extractors) return config.content.options.extractors
95+
96+
return {}
97+
})()
98+
99+
let extractors = {}
100+
101+
// Functions
102+
if (typeof extract === 'function') {
103+
extractors.DEFAULT = extract
104+
}
105+
106+
// Arrays
107+
else if (Array.isArray(extract)) {
108+
for (let { extensions, extractor } of extract ?? []) {
109+
for (let extension of extensions) {
110+
extractors[extension] = extractor
111+
}
112+
}
113+
}
114+
115+
// Objects
116+
else if (typeof extract === 'object' && extract !== null) {
117+
Object.assign(extractors, extract)
118+
}
119+
120+
return extractors
121+
})(),
122+
123+
transform: (() => {
124+
let transform = (() => {
125+
if (config.purge?.transform) return config.purge.transform
126+
if (config.content?.transform) return config.content.transform
127+
128+
if (config.purge?.transform?.DEFAULT) return config.purge.transform.DEFAULT
129+
if (config.content?.transform?.DEFAULT) return config.content.transform.DEFAULT
130+
131+
return {}
132+
})()
133+
134+
let transformers = {}
135+
136+
if (typeof transform === 'function') {
137+
transformers.DEFAULT = transform
138+
}
139+
140+
if (typeof transform === 'object' && transform !== null) {
141+
Object.assign(transformers, transform)
142+
}
143+
144+
return transformers
145+
})(),
146+
}
147+
148+
return config
149+
}

src/util/resolveConfig.js

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import corePluginList from '../corePluginList'
33
import configurePlugins from './configurePlugins'
44
import defaultConfig from '../../stubs/defaultConfig.stub'
55
import colors from '../public/colors'
6-
import log from './log'
76
import { defaults } from './defaults'
87
import { toPath } from './toPath'
8+
import { normalizeConfig } from './normalizeConfig'
99

1010
function isFunction(input) {
1111
return typeof input === 'function'
@@ -221,55 +221,3 @@ export default function resolveConfig(configs) {
221221
)
222222
)
223223
}
224-
225-
function normalizeConfig(config) {
226-
log.warn('purge-deprecation', [
227-
'The `purge` option in your tailwind.config.js file has been deprecated.',
228-
'Please rename this to `content` instead.',
229-
])
230-
231-
config.content = {
232-
content: (() => {
233-
let { content, purge } = config
234-
235-
if (Array.isArray(purge)) return purge
236-
if (Array.isArray(purge?.content)) return purge.content
237-
if (Array.isArray(content)) return content
238-
if (Array.isArray(content?.content)) return content.content
239-
240-
return []
241-
})(),
242-
safelist: (() => {
243-
let { content, purge } = config
244-
245-
let [safelistKey, safelistPaths] = (() => {
246-
if (Array.isArray(content?.safelist)) return ['content.safelist', content.safelist]
247-
if (Array.isArray(purge?.safelist)) return ['purge.safelist', purge.safelist]
248-
if (Array.isArray(purge?.options?.safelist))
249-
return ['purge.options.safelist', purge.options.safelist]
250-
return [null, []]
251-
})()
252-
253-
return safelistPaths.map((content) => {
254-
if (typeof content === 'string') {
255-
return { raw: content, extension: 'html' }
256-
}
257-
258-
if (content instanceof RegExp) {
259-
throw new Error(
260-
`Values inside '${safelistKey}' can only be of type 'string', found 'regex'.`
261-
)
262-
}
263-
264-
throw new Error(
265-
`Values inside '${safelistKey}' can only be of type 'string', found '${typeof content}'.`
266-
)
267-
})
268-
})(),
269-
extract: config.content?.extract || config.purge?.extract || {},
270-
options: config.content?.options || config.purge?.options || {},
271-
transform: config.content?.transform || config.purge?.transform || {},
272-
}
273-
274-
return config
275-
}

tests/normalize-config.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { run, css } from './util/run'
2+
3+
it.each`
4+
config
5+
${{ purge: [{ raw: 'text-center' }] }}
6+
${{ purge: { content: [{ raw: 'text-center' }] } }}
7+
${{ content: { content: [{ raw: 'text-center' }] } }}
8+
`('should normalize content $config', ({ config }) => {
9+
return run('@tailwind utilities', config).then((result) => {
10+
return expect(result.css).toMatchFormattedCss(css`
11+
.text-center {
12+
text-align: center;
13+
}
14+
`)
15+
})
16+
})
17+
18+
it.each`
19+
config
20+
${{ purge: { safelist: ['text-center'] } }}
21+
${{ purge: { options: { safelist: ['text-center'] } } }}
22+
${{ content: { safelist: ['text-center'] } }}
23+
`('should normalize safelist $config', ({ config }) => {
24+
return run('@tailwind utilities', config).then((result) => {
25+
return expect(result.css).toMatchFormattedCss(css`
26+
.text-center {
27+
text-align: center;
28+
}
29+
`)
30+
})
31+
})
32+
33+
it.each`
34+
config
35+
${{ content: [{ raw: 'text-center' }], purge: { extract: () => ['font-bold'] } }}
36+
${{ content: [{ raw: 'text-center' }], purge: { extract: { DEFAULT: () => ['font-bold'] } } }}
37+
${{ content: [{ raw: 'text-center' }], purge: { options: { defaultExtractor: () => ['font-bold'] } } }}
38+
${{ content: [{ raw: 'text-center' }], purge: { options: { extractors: [{ extractor: () => ['font-bold'], extensions: ['html'] }] } } }}
39+
${{ content: [{ raw: 'text-center' }], purge: { extract: { html: () => ['font-bold'] } } }}
40+
`('should normalize extractors $config', ({ config }) => {
41+
return run('@tailwind utilities', config).then((result) => {
42+
return expect(result.css).toMatchFormattedCss(css`
43+
.font-bold {
44+
font-weight: 700;
45+
}
46+
`)
47+
})
48+
})

0 commit comments

Comments
 (0)