UNPKG

postcss-ordered-values

Version:

Ensure values are ordered consistently in your CSS.

191 lines (169 loc) • 6.25 kB
import { tokenizeValue } from './lib/tokenize.js'; import { normalizeGridAutoFlow, normalizeGridColumnRow } from './rules/grid.js'; import animation from './rules/animation.js'; import border from './rules/border.js'; import boxShadow from './rules/boxShadow.js'; import flexFlow from './rules/flexFlow.js'; import transition from './rules/transition.js'; import listStyle from './rules/listStyle.js'; import column from './rules/columns.js'; import vendorUnprefixed from './lib/vendorUnprefixed.js'; /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const borderProcessor = (parsed) => border(parsed.terms); /** @type {[string, (parsed: ReturnType<typeof tokenizeValue>) => string | null][]} */ const borderRules = [ ['border', borderProcessor], ['border-block', borderProcessor], ['border-inline', borderProcessor], ['border-block-end', borderProcessor], ['border-block-start', borderProcessor], ['border-inline-end', borderProcessor], ['border-inline-start', borderProcessor], ['border-top', borderProcessor], ['border-right', borderProcessor], ['border-bottom', borderProcessor], ['border-left', borderProcessor], ]; /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const gridAutoFlowProcessor = (parsed) => normalizeGridAutoFlow(parsed.terms); /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const gridLineProcessor = (parsed) => normalizeGridColumnRow(parsed.terms, 2); /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const gridLonghandProcessor = (parsed) => normalizeGridColumnRow(parsed.terms, 1); /** @type {[string, (parsed: ReturnType<typeof tokenizeValue>) => string | null][]} */ const grid = [ ['grid-auto-flow', gridAutoFlowProcessor], ['grid-column', gridLineProcessor], ['grid-row', gridLineProcessor], ['grid-row-start', gridLonghandProcessor], ['grid-row-end', gridLonghandProcessor], ['grid-column-start', gridLonghandProcessor], ['grid-column-end', gridLonghandProcessor], ]; /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const columnRuleProcessor = borderProcessor; /** @type {[string, (parsed: ReturnType<typeof tokenizeValue>) => string | null][]} */ const columnRules = [ ['column-rule', columnRuleProcessor], ['columns', (parsed) => column(parsed.terms)], ]; /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const flexFlowProcessor = (parsed) => flexFlow(parsed.terms); /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const listStyleProcessor = (parsed) => listStyle(parsed.terms); /** @type {(parsed: ReturnType<typeof tokenizeValue>) => string | null} */ const outlineProcessor = (parsed) => border(parsed.terms, true); const listRules = new Set(['animation', 'box-shadow', 'transition']); /** @type {Map<string, (parsed: ReturnType<typeof tokenizeValue>) => string | null>} */ const rules = new Map([ ['animation', animation], ['outline', outlineProcessor], ['box-shadow', boxShadow], ['flex-flow', flexFlowProcessor], ['list-style', listStyleProcessor], ['transition', transition], ...borderRules, ...grid, ...columnRules, ]); /** * @param {import('postcss').Declaration} decl * @return {string} */ function getValue(decl) { let value = decl.value; const raws = decl.raws; if (raws && raws.value && raws.value.raw && raws.value.value === decl.value) { value = raws.value.raw; } return value; } /** * @param {import('postcss').Declaration} decl * @param {string} value */ function assignValue(decl, value) { if (decl.value !== value) { decl.value = value; } if (decl.raws?.value?.raw && decl.raws.value.value !== value) { decl.raws.value = { raw: value, value }; } } /** * @return {import('postcss').Plugin} */ function pluginCreator() { return { postcssPlugin: 'postcss-ordered-values', prepare() { /** @type {Map<Function, Map<string, string>>} */ const cache = new Map(); return { /** * @param {import('postcss').Root} css */ OnceExit(css) { css.walkDecls((decl) => { const prop = decl.prop; let processor = rules.get(prop); let normalizedProp = prop; if (!processor) { const lower = prop.toLowerCase(); processor = rules.get(lower); normalizedProp = lower; if ( !processor && prop.charCodeAt(0) === 45 /* '-' */ && prop.charCodeAt(1) !== 45 /* '-' */ ) { normalizedProp = vendorUnprefixed(lower); processor = rules.get(normalizedProp); } } if (!processor) { return; } const value = getValue(decl); let processorCache = cache.get(processor); if (processorCache === undefined) { processorCache = new Map(); cache.set(processor, processorCache); } if (processorCache.has(value)) { assignValue( decl, /** @type {string} */ (processorCache.get(value)) ); return; } if (value.length < 2 || !/[, \t\n\r\f\/]/v.test(value)) { processorCache.set(value, value); assignValue(decl, value); return; } const parsed = tokenizeValue(value); if ( parsed.terms.length < 2 || parsed.abort || (!listRules.has(normalizedProp) && parsed.arguments.length > 1) ) { processorCache.set(value, value); assignValue(decl, value); return; } const processed = processor(parsed); const result = processed ?? value; assignValue(decl, result); processorCache.set(value, result); }); }, }; }, }; } /** @type {true} */ pluginCreator.postcss = true; const moduleExports = pluginCreator; export { moduleExports as default, moduleExports as 'module.exports' };