Mirror: CSS prefixing helpers in less than 1KB 馃寛
at main 133 lines 4.5 kB view raw
1#!/usr/bin/env node 2 3const prefixMap = require('inline-style-prefixer/lib/data').default.prefixMap; 4const mdnProperties = require('mdn-data/css/properties.json'); 5 6const PREFIX_MS = 'ms'; 7const PREFIX_MOZ = 'Moz'; 8const PREFIX_WEBKIT = 'Webkit'; 9const prefixPropRe = /^-(ms|moz|webkit)-/; 10 11/** A list of all properties that have to be prefixed */ 12const properties = Object.keys(prefixMap) 13 .map(prop => ({ 14 // Convert inline-style-based name to CSS property name 15 name: prop.replace(/[A-Z]/g, '-$&').toLowerCase(), 16 // This describes what kind of prefixes are necessary: 17 ms: !!prefixMap[prop].includes(PREFIX_MS), 18 moz: !!prefixMap[prop].includes(PREFIX_MOZ), 19 webkit: !!prefixMap[prop].includes(PREFIX_WEBKIT), 20 })) 21 // Omit CSS properties that are not listed by MDN or are obsolete 22 .filter(({ name }) => ( 23 mdnProperties[name] && 24 mdnProperties[name].status !== 'obsolete' && 25 // Skip some properties that aren't widely supported or don't need prefixing: 26 name !== 'backdrop-filter' && 27 name !== 'filter' && 28 // Skip some properties that are obsolete: 29 name !== 'scroll-snap-points-x' && 30 name !== 'scroll-snap-points-y' && 31 name !== 'scroll-snap-points-destination' && 32 name !== 'scroll-snap-points-coordinate' && 33 name !== 'flow-into' && 34 name !== 'flow-from' && 35 name !== 'wrap-flow' && 36 name !== 'wrap-through' && 37 name !== 'wrap-margin' 38 )); 39 40// See SUPPORT.md on background-clip 41properties.push({ 42 name: 'background-clip', 43 ms: false, 44 moz: false, 45 webkit: true 46}); 47 48// These are supported in Firefox, Chrome, and Safari 49// NOTE: Their variants with before/after are not supported 50// by Firefox and should be avoided 51properties.push(...[ 52 'border-start', 53 'border-start-color', 54 'border-start-style', 55 'border-start-width', 56 'border-end', 57 'border-end-color', 58 'border-end-style', 59 'border-end-width', 60 'border-start-start-radius', 61 'border-start-end-radius', 62 'border-end-start-radius', 63 'border-end-end-radius', 64].map(name => ({ name, ms: false, moz: true, webkit: true }))); 65 66/** A list of stable, non-prefixable property names */ 67const stablePropertyNames = Object.keys(mdnProperties) 68 .filter(x => ( 69 // Only include non-obsolete CSS properties 70 mdnProperties[x].status !== 'obsolete' && 71 x !== 'all' && 72 x !== '--*' && 73 // Skip some properties that aren't widely supported: 74 x !== 'text-decoration-skip-ink' && 75 x !== 'text-decoration-thickness' && 76 // Exclude prefixed properties 77 !prefixPropRe.test(x) && 78 // Exclude properties that are to be prefixed (i.e. non-standard) 79 !properties.some(({ name }) => name === x) 80 )); 81 82/** Finds the minimum starting substring that uniquely identifier a property out of all known CSS properties */ 83const findMinimumSubstr = name => { 84 for (let i = 2, l = name.length; i < l; i++) { 85 const substr = name.slice(0, i); 86 // Check for any name that conflicts with the substring in all known CSS properties 87 if (stablePropertyNames.every(x => x === name || !x.startsWith(substr))) { 88 return substr; 89 } 90 } 91 92 return name; 93}; 94 95/** Lists each prefixed property with the minimum substring that is needed to uniquely identity it */ 96const prefixPatterns = properties.map(prop => ({ 97 ...prop, 98 name: findMinimumSubstr(prop.name) 99})); 100 101/** For Webkit prefixes, these properties will need some values to be prefixed */ 102const prefixValuePatterns = ['position', 'background-clip'].map(findMinimumSubstr); 103 104/** Accepts a filter and builds a list of names in `prefixPatterns` */ 105const reducePrefixes = (filter = x => !!x) => { 106 const set = prefixPatterns.reduce((acc, prop) => { 107 if (filter(prop)) acc.add(prop.name); 108 return acc; 109 }, new Set()); 110 111 return [...set].sort((a, b) => { 112 if (a.length === b.length) return a.localeCompare(b); 113 return a.length - b.length; 114 }); 115}; 116 117/** Creates a regex matching property names */ 118const buildRegex = groups => `^(${groups.join('|')})`; 119 120// Create all prefix sets for each prefix 121const msPrefixes = buildRegex(reducePrefixes(x => x.ms)); 122const mozPrefixes = buildRegex(reducePrefixes(x => x.moz)); 123const webkitPrefixes = buildRegex(reducePrefixes(x => x.webkit)); 124 125// Create a regex for webkit value transforms 126const webkitValuePrefix = buildRegex(prefixValuePatterns); 127 128module.exports = ` 129var msPrefixRe = /${msPrefixes}/; 130var mozPrefixRe = /${mozPrefixes}/; 131var webkitPrefixRe = /${webkitPrefixes}/; 132var webkitValuePrefixRe = /${webkitValuePrefix}/; 133`.trim();