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 'border-start-start-radius',
67 'border-start-end-radius',
68 'border-end-start-radius',
69 'border-end-end-radius',
70].map(name => ({ name, ms: false, moz: true, webkit: true })));
71
72/** A list of stable, non-prefixable property names */
73const stablePropertyNames = Object.keys(mdnProperties)
74 .filter(x => (
75 // Only include non-obsolete CSS properties
76 mdnProperties[x].status !== 'obsolete' &&
77 x !== 'all' &&
78 x !== '--*' &&
79 // Skip some properties that aren't widely supported:
80 x !== 'text-decoration-skip-ink' &&
81 x !== 'text-decoration-thickness' &&
82 // Exclude prefixed properties
83 !prefixPropRe.test(x) &&
84 // Exclude properties that are to be prefixed (i.e. non-standard)
85 !properties.some(({ name }) => name === x)
86 ));
87
88/** Lists each prefixed property with the minimum substring that is needed to uniquely identity it */
89const prefixPatterns = properties
90 .map(prop => {
91 let name = prop.name;
92 for (let i = 2, l = name.length; i < l; i++) {
93 const substr = name.slice(0, i);
94 // Check for any name that conflicts with the substring in all known CSS properties
95 if (stablePropertyNames.every(x => x === name || !x.startsWith(substr))) {
96 name = substr;
97 break;
98 }
99 }
100
101 return { ...prop, name };
102 });
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();
112};
113
114const buildRegex = groups => `^(${groups.join('|')})`;
115
116// Create all prefix sets for each prefix
117const msPrefixes = buildRegex(reducePrefixes(x => x.ms));
118const mozPrefixes = buildRegex(reducePrefixes(x => x.moz));
119const webkitPrefixes = buildRegex(reducePrefixes(x => x.webkit));
120
121module.exports = `
122var msPrefixRe = /${msPrefixes}/;
123var mozPrefixRe = /${mozPrefixes}/;
124var webkitPrefixRe = /${webkitPrefixes}/;
125`.trim();