diff options
Diffstat (limited to 'node_modules/tailwindcss/src/util/normalizeConfig.js')
| -rw-r--r-- | node_modules/tailwindcss/src/util/normalizeConfig.js | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/node_modules/tailwindcss/src/util/normalizeConfig.js b/node_modules/tailwindcss/src/util/normalizeConfig.js new file mode 100644 index 0000000..7e1a592 --- /dev/null +++ b/node_modules/tailwindcss/src/util/normalizeConfig.js @@ -0,0 +1,301 @@ +import { flagEnabled } from '../featureFlags' +import log, { dim } from './log' + +export function normalizeConfig(config) { + // Quick structure validation + /** + * type FilePath = string + * type RawFile = { raw: string, extension?: string } + * type ExtractorFn = (content: string) => Array<string> + * type TransformerFn = (content: string) => string + * + * type Content = + * | Array<FilePath | RawFile> + * | { + * files: Array<FilePath | RawFile>, + * extract?: ExtractorFn | { [extension: string]: ExtractorFn } + * transform?: TransformerFn | { [extension: string]: TransformerFn } + * } + */ + let valid = (() => { + // `config.purge` should not exist anymore + if (config.purge) { + return false + } + + // `config.content` should exist + if (!config.content) { + return false + } + + // `config.content` should be an object or an array + if ( + !Array.isArray(config.content) && + !(typeof config.content === 'object' && config.content !== null) + ) { + return false + } + + // When `config.content` is an array, it should consist of FilePaths or RawFiles + if (Array.isArray(config.content)) { + return config.content.every((path) => { + // `path` can be a string + if (typeof path === 'string') return true + + // `path` can be an object { raw: string, extension?: string } + // `raw` must be a string + if (typeof path?.raw !== 'string') return false + + // `extension` (if provided) should also be a string + if (path?.extension && typeof path?.extension !== 'string') { + return false + } + + return true + }) + } + + // When `config.content` is an object + if (typeof config.content === 'object' && config.content !== null) { + // Only `files`, `relative`, `extract`, and `transform` can exist in `config.content` + if ( + Object.keys(config.content).some( + (key) => !['files', 'relative', 'extract', 'transform'].includes(key) + ) + ) { + return false + } + + // `config.content.files` should exist of FilePaths or RawFiles + if (Array.isArray(config.content.files)) { + if ( + !config.content.files.every((path) => { + // `path` can be a string + if (typeof path === 'string') return true + + // `path` can be an object { raw: string, extension?: string } + // `raw` must be a string + if (typeof path?.raw !== 'string') return false + + // `extension` (if provided) should also be a string + if (path?.extension && typeof path?.extension !== 'string') { + return false + } + + return true + }) + ) { + return false + } + + // `config.content.extract` is optional, and can be a Function or a Record<String, Function> + 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 + } + + // `config.content.transform` is optional, and can be a Function or a Record<String, Function> + 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 + } + + // `config.content.relative` is optional and can be a boolean + 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', + ]) + } + + // Normalize the `safelist` + 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 [] + })() + + // Normalize the `blocklist` + 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 [] + })() + + // Normalize prefix option + 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 ?? '' + } + + // Normalize the `content` + 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 + } + + // Functions + if (typeof extract === 'function') { + extractors.DEFAULT = extract + } + + // Arrays + else if (Array.isArray(extract)) { + for (let { extensions, extractor } of extract ?? []) { + for (let extension of extensions) { + extractors[extension] = extractor + } + } + } + + // Objects + 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 + })(), + } + + // Validate globs to prevent bogus globs. + // E.g.: `./src/*.{html}` is invalid, the `{html}` should just be `html` + 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.`, + // TODO: Add https://tw.wtf/invalid-glob-braces + ]) + break + } + } + + return config +} |