UNPKG

2.98 kBJavaScriptView Raw
1'use strict';
2const browserslist = require('browserslist');
3const valueParser = require('postcss-value-parser');
4
5const regexLowerCaseUPrefix = /^u(?=\+)/;
6
7/**
8 * @param {string} range
9 * @return {string}
10 */
11function unicode(range) {
12 const values = range.slice(2).split('-');
13
14 if (values.length < 2) {
15 return range;
16 }
17
18 const left = values[0].split('');
19 const right = values[1].split('');
20
21 if (left.length !== right.length) {
22 return range;
23 }
24
25 const merged = mergeRangeBounds(left, right);
26
27 if (merged) {
28 return merged;
29 }
30
31 return range;
32}
33/**
34 * @param {string[]} left
35 * @param {string[]} right
36 * @return {false|string}
37 */
38function mergeRangeBounds(left, right) {
39 let questionCounter = 0;
40 let group = 'u+';
41 for (const [index, value] of left.entries()) {
42 if (value === right[index] && questionCounter === 0) {
43 group = group + value;
44 } else if (value === '0' && right[index] === 'f') {
45 questionCounter++;
46 group = group + '?';
47 } else {
48 return false;
49 }
50 }
51 // The maximum number of wildcard characters (?) for ranges is 5.
52 if (questionCounter < 6) {
53 return group;
54 } else {
55 return false;
56 }
57}
58
59/**
60 * IE and Edge before 16 version ignore the unicode-range if the 'U' is lowercase
61 *
62 * https://caniuse.com/#search=unicode-range
63 *
64 * @param {string} browser
65 * @return {boolean}
66 */
67function hasLowerCaseUPrefixBug(browser) {
68 return browserslist('ie <=11, edge <= 15').includes(browser);
69}
70
71/**
72 * @param {string} value
73 * @return {string}
74 */
75function transform(value, isLegacy = false) {
76 return valueParser(value)
77 .walk((child) => {
78 if (child.type === 'unicode-range') {
79 const transformed = unicode(child.value.toLowerCase());
80
81 child.value = isLegacy
82 ? transformed.replace(regexLowerCaseUPrefix, 'U')
83 : transformed;
84 }
85
86 return false;
87 })
88 .toString();
89}
90
91/**
92 * @type {import('postcss').PluginCreator<void>}
93 * @return {import('postcss').Plugin}
94 */
95function pluginCreator() {
96 return {
97 postcssPlugin: 'postcss-normalize-unicode',
98 /** @param {import('postcss').Result & {opts: browserslist.Options}} result*/
99 prepare(result) {
100 const cache = new Map();
101 const resultOpts = result.opts || {};
102 const browsers = browserslist(null, {
103 stats: resultOpts.stats,
104 path: __dirname,
105 env: resultOpts.env,
106 });
107 const isLegacy = browsers.some(hasLowerCaseUPrefixBug);
108
109 return {
110 OnceExit(css) {
111 css.walkDecls(/^unicode-range$/i, (decl) => {
112 const value = decl.value;
113
114 if (cache.has(value)) {
115 decl.value = cache.get(value);
116
117 return;
118 }
119
120 const newValue = transform(value, isLegacy);
121
122 decl.value = newValue;
123 cache.set(value, newValue);
124 });
125 },
126 };
127 },
128 };
129}
130
131pluginCreator.postcss = true;
132module.exports = pluginCreator;