|
import { flagEnabled } from '../featureFlags' |
|
import log, { dim } from './log' |
|
|
|
export function normalizeConfig(config) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let valid = (() => { |
|
|
|
if (config.purge) { |
|
return false |
|
} |
|
|
|
|
|
if (!config.content) { |
|
return false |
|
} |
|
|
|
|
|
if ( |
|
!Array.isArray(config.content) && |
|
!(typeof config.content === 'object' && config.content !== null) |
|
) { |
|
return false |
|
} |
|
|
|
|
|
if (Array.isArray(config.content)) { |
|
return config.content.every((path) => { |
|
|
|
if (typeof path === 'string') return true |
|
|
|
|
|
|
|
if (typeof path?.raw !== 'string') return false |
|
|
|
|
|
if (path?.extension && typeof path?.extension !== 'string') { |
|
return false |
|
} |
|
|
|
return true |
|
}) |
|
} |
|
|
|
|
|
if (typeof config.content === 'object' && config.content !== null) { |
|
|
|
if ( |
|
Object.keys(config.content).some( |
|
(key) => !['files', 'relative', 'extract', 'transform'].includes(key) |
|
) |
|
) { |
|
return false |
|
} |
|
|
|
|
|
if (Array.isArray(config.content.files)) { |
|
if ( |
|
!config.content.files.every((path) => { |
|
|
|
if (typeof path === 'string') return true |
|
|
|
|
|
|
|
if (typeof path?.raw !== 'string') return false |
|
|
|
|
|
if (path?.extension && typeof path?.extension !== 'string') { |
|
return false |
|
} |
|
|
|
return true |
|
}) |
|
) { |
|
return false |
|
} |
|
|
|
|
|
if (typeof config.content.extract === 'object') { |
|
for (let value of Object.values(config.content.extract)) { |
|
if (typeof value !== 'function') { |
|
return false |
|
} |
|
} |
|
} else if ( |
|
!(config.content.extract === undefined || typeof config.content.extract === 'function') |
|
) { |
|
return false |
|
} |
|
|
|
|
|
if (typeof config.content.transform === 'object') { |
|
for (let value of Object.values(config.content.transform)) { |
|
if (typeof value !== 'function') { |
|
return false |
|
} |
|
} |
|
} else if ( |
|
!( |
|
config.content.transform === undefined || typeof config.content.transform === 'function' |
|
) |
|
) { |
|
return false |
|
} |
|
|
|
|
|
if ( |
|
typeof config.content.relative !== 'boolean' && |
|
typeof config.content.relative !== 'undefined' |
|
) { |
|
return false |
|
} |
|
} |
|
|
|
return true |
|
} |
|
|
|
return false |
|
})() |
|
|
|
if (!valid) { |
|
log.warn('purge-deprecation', [ |
|
'The `purge`/`content` options have changed in Tailwind CSS v3.0.', |
|
'Update your configuration file to eliminate this warning.', |
|
'https://tailwindcss.com/docs/upgrade-guide#configure-content-sources', |
|
]) |
|
} |
|
|
|
|
|
config.safelist = (() => { |
|
let { content, purge, safelist } = config |
|
|
|
if (Array.isArray(safelist)) return safelist |
|
if (Array.isArray(content?.safelist)) return content.safelist |
|
if (Array.isArray(purge?.safelist)) return purge.safelist |
|
if (Array.isArray(purge?.options?.safelist)) return purge.options.safelist |
|
|
|
return [] |
|
})() |
|
|
|
|
|
config.blocklist = (() => { |
|
let { blocklist } = config |
|
|
|
if (Array.isArray(blocklist)) { |
|
if (blocklist.every((item) => typeof item === 'string')) { |
|
return blocklist |
|
} |
|
|
|
log.warn('blocklist-invalid', [ |
|
'The `blocklist` option must be an array of strings.', |
|
'https://tailwindcss.com/docs/content-configuration#discarding-classes', |
|
]) |
|
} |
|
|
|
return [] |
|
})() |
|
|
|
|
|
if (typeof config.prefix === 'function') { |
|
log.warn('prefix-function', [ |
|
'As of Tailwind CSS v3.0, `prefix` cannot be a function.', |
|
'Update `prefix` in your configuration to be a string to eliminate this warning.', |
|
'https://tailwindcss.com/docs/upgrade-guide#prefix-cannot-be-a-function', |
|
]) |
|
config.prefix = '' |
|
} else { |
|
config.prefix = config.prefix ?? '' |
|
} |
|
|
|
|
|
config.content = { |
|
relative: (() => { |
|
let { content } = config |
|
|
|
if (content?.relative) { |
|
return content.relative |
|
} |
|
|
|
return flagEnabled(config, 'relativeContentPathsByDefault') |
|
})(), |
|
|
|
files: (() => { |
|
let { content, purge } = config |
|
|
|
if (Array.isArray(purge)) return purge |
|
if (Array.isArray(purge?.content)) return purge.content |
|
if (Array.isArray(content)) return content |
|
if (Array.isArray(content?.content)) return content.content |
|
if (Array.isArray(content?.files)) return content.files |
|
|
|
return [] |
|
})(), |
|
|
|
extract: (() => { |
|
let extract = (() => { |
|
if (config.purge?.extract) return config.purge.extract |
|
if (config.content?.extract) return config.content.extract |
|
|
|
if (config.purge?.extract?.DEFAULT) return config.purge.extract.DEFAULT |
|
if (config.content?.extract?.DEFAULT) return config.content.extract.DEFAULT |
|
|
|
if (config.purge?.options?.extractors) return config.purge.options.extractors |
|
if (config.content?.options?.extractors) return config.content.options.extractors |
|
|
|
return {} |
|
})() |
|
|
|
let extractors = {} |
|
|
|
let defaultExtractor = (() => { |
|
if (config.purge?.options?.defaultExtractor) { |
|
return config.purge.options.defaultExtractor |
|
} |
|
|
|
if (config.content?.options?.defaultExtractor) { |
|
return config.content.options.defaultExtractor |
|
} |
|
|
|
return undefined |
|
})() |
|
|
|
if (defaultExtractor !== undefined) { |
|
extractors.DEFAULT = defaultExtractor |
|
} |
|
|
|
|
|
if (typeof extract === 'function') { |
|
extractors.DEFAULT = extract |
|
} |
|
|
|
|
|
else if (Array.isArray(extract)) { |
|
for (let { extensions, extractor } of extract ?? []) { |
|
for (let extension of extensions) { |
|
extractors[extension] = extractor |
|
} |
|
} |
|
} |
|
|
|
|
|
else if (typeof extract === 'object' && extract !== null) { |
|
Object.assign(extractors, extract) |
|
} |
|
|
|
return extractors |
|
})(), |
|
|
|
transform: (() => { |
|
let transform = (() => { |
|
if (config.purge?.transform) return config.purge.transform |
|
if (config.content?.transform) return config.content.transform |
|
|
|
if (config.purge?.transform?.DEFAULT) return config.purge.transform.DEFAULT |
|
if (config.content?.transform?.DEFAULT) return config.content.transform.DEFAULT |
|
|
|
return {} |
|
})() |
|
|
|
let transformers = {} |
|
|
|
if (typeof transform === 'function') { |
|
transformers.DEFAULT = transform |
|
} |
|
|
|
if (typeof transform === 'object' && transform !== null) { |
|
Object.assign(transformers, transform) |
|
} |
|
|
|
return transformers |
|
})(), |
|
} |
|
|
|
|
|
|
|
for (let file of config.content.files) { |
|
if (typeof file === 'string' && /{([^,]*?)}/g.test(file)) { |
|
log.warn('invalid-glob-braces', [ |
|
`The glob pattern ${dim(file)} in your Tailwind CSS configuration is invalid.`, |
|
`Update it to ${dim(file.replace(/{([^,]*?)}/g, '$1'))} to silence this warning.`, |
|
|
|
]) |
|
break |
|
} |
|
} |
|
|
|
return config |
|
} |
|
|