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