Skip to content

Commit f9f9def

Browse files
committed
Support less and sass syntax for variables
1 parent d5025d9 commit f9f9def

File tree

2 files changed

+214
-111
lines changed

2 files changed

+214
-111
lines changed

index.js

Lines changed: 153 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -5,135 +5,161 @@ const colorObject = require('color-name');
55
const parserColor = require('parse-color');
66
const nearestColor = require('nearest-color');
77

8-
module.exports = postcss.plugin('postcss-extract-value', (opts) => {
9-
// Fix for Node 4
10-
const params = opts || {};
8+
const colorList = {};
119

12-
// Options
13-
const filterByProps = params.filterByProps;
14-
const onlyColor = params.onlyColor;
15-
const scope = params.scope || ':root';
16-
const templateVariableName = params.templateVariableName || '';
10+
Object.keys(colorObject).forEach((key) => {
11+
colorList[key] = {
12+
r: colorObject[key][0],
13+
g: colorObject[key][1],
14+
b: colorObject[key][2],
15+
};
16+
});
17+
const findColor = nearestColor.from(colorList);
1718

19+
function checkProp(filter, prop) {
20+
return filter.indexOf(prop) > -1;
21+
}
1822

19-
const colorList = {};
20-
const colorNameList = Object.keys(colorObject);
23+
function isColor(reColorKeywords, value) {
24+
const reCheck = new RegExp(`${/#\w+|rgba?|hsla?/.source}|${reColorKeywords.source}`, 'g');
25+
return reCheck.test(value);
26+
}
2127

22-
colorNameList.forEach((key) => {
23-
colorList[key] = {
24-
r: colorObject[key][0],
25-
g: colorObject[key][1],
26-
b: colorObject[key][2],
27-
};
28-
});
29-
const findColor = nearestColor.from(colorList);
30-
const variablesListCounter = {};
28+
function extractColor(reExtract, value) {
29+
const resultArray = [];
30+
let result = reExtract.exec(value);
3131

32-
// Cache RegExp
33-
const reColorKeywords = new RegExp(colorNameList.join('|'));
34-
const reCSSVariable = /^var\(-{2}\w{1}[\w+-]*/;
35-
const reHex = /#(\w{6}|\w{3})/;
36-
const reRgb = /rgba?\([\d,.\s]+\)/;
37-
const reHsl = /hsla?\(\s?[0-9]{1,3},\s?([0-9]{1,3}%,?\s?){2}([0-9.]+)?\)/;
38-
const reExtract = new RegExp(`${reHex.source}|${reRgb.source}|${reHsl.source}|${reColorKeywords.source}`, 'g');
39-
40-
function isColor(value) {
41-
const reCheck = new RegExp(`${/#\w+|rgba?|hsla?/.source}|${reColorKeywords.source}`, 'g');
42-
return reCheck.test(value);
32+
while (result) {
33+
resultArray.push(result[0]);
34+
result = reExtract.exec(value);
4335
}
36+
return resultArray;
37+
}
4438

45-
function extractColor(value) {
46-
const resultArray = [];
47-
let result = reExtract.exec(value);
39+
function colorNameVariable(templateVariableName, value) {
40+
const variable = {};
41+
let nearestColorValue = {};
42+
const parsedColor = parserColor(value);
4843

49-
while (result) {
50-
resultArray.push(result[0]);
51-
result = reExtract.exec(value);
52-
}
53-
return resultArray;
44+
if (parsedColor.hex) {
45+
nearestColorValue = parserColor(findColor(parsedColor.hex).value);
5446
}
5547

56-
function checkProp(filter, prop) {
57-
return filter.indexOf(prop) > -1;
58-
}
48+
if (nearestColorValue) {
49+
variable.colorKeyword = nearestColorValue.keyword;
5950

60-
function colorNameVariable(value) {
61-
const variable = {};
62-
let nearestColorValue = {};
63-
const parsedColor = parserColor(value);
64-
65-
if (parsedColor.hex) {
66-
nearestColorValue = parserColor(findColor(parsedColor.hex).value);
67-
}
68-
69-
if (nearestColorValue) {
70-
variable.colorKeyword = nearestColorValue.keyword;
71-
72-
if (templateVariableName.indexOf('[tint]') > -1) {
73-
if (nearestColorValue.hsl[2] > parsedColor.hsl[2]) {
74-
variable.tint = 'dark';
75-
} else if (nearestColorValue.hsl[2] < parsedColor.hsl[2]) {
76-
variable.tint = 'light';
77-
} else {
78-
variable.tint = '';
79-
}
51+
if (templateVariableName.indexOf('[tint]') > -1) {
52+
if (nearestColorValue.hsl[2] > parsedColor.hsl[2]) {
53+
variable.tint = 'dark';
54+
} else if (nearestColorValue.hsl[2] < parsedColor.hsl[2]) {
55+
variable.tint = 'light';
56+
} else {
57+
variable.tint = '';
58+
}
8059

81-
if (variable.tint) {
82-
if (templateVariableName.indexOf('[tint]') > 0) {
83-
variable.tint = `-${variable.tint}`;
84-
}
60+
if (variable.tint) {
61+
if (templateVariableName.indexOf('[tint]') > 0) {
62+
variable.tint = `-${variable.tint}`;
8563
}
8664
}
87-
if (templateVariableName.indexOf('[colorKeyword]') > 0 &&
88-
!(templateVariableName.indexOf('[tint]') === 0 &&
89-
!variable.tint)) {
90-
variable.colorKeyword = `-${variable.colorKeyword}`;
91-
}
9265
}
93-
94-
return variable;
66+
if (templateVariableName.indexOf('[colorKeyword]') > 0 &&
67+
!(templateVariableName.indexOf('[tint]') === 0 &&
68+
!variable.tint)) {
69+
variable.colorKeyword = `-${variable.colorKeyword}`;
70+
}
9571
}
9672

97-
function makeNameByTemplate(value, prop) {
98-
let nameVariables = [];
99-
let result = templateVariableName;
73+
return variable;
74+
}
10075

101-
if (onlyColor) {
102-
nameVariables = colorNameVariable(value);
103-
} else if (templateVariableName.indexOf('[propertyName]')) {
104-
nameVariables.propertyName = prop;
105-
}
106-
Object.keys(nameVariables).forEach((key) => {
107-
result = result.replace(`[${key}]`, nameVariables[key]);
108-
});
109-
return result;
76+
function makeNameByTemplate(templateVariableName, onlyColor, value, prop) {
77+
let nameVariables = [];
78+
let result = templateVariableName;
79+
80+
if (onlyColor) {
81+
nameVariables = colorNameVariable(templateVariableName, value);
82+
} else if (templateVariableName.indexOf('[propertyName]')) {
83+
nameVariables.propertyName = prop;
11084
}
85+
Object.keys(nameVariables).forEach((key) => {
86+
result = result.replace(`[${key}]`, nameVariables[key]);
87+
});
88+
return result;
89+
}
11190

112-
function makeCSSVariable(prop, num, value) {
113-
let variableName = '';
114-
let result = '';
91+
function addVariablePrefix(variablePrefix, variableSyntax, variable) {
92+
const prefix = variablePrefix[variableSyntax];
93+
return `${prefix || variablePrefix.default}${variable}`;
94+
}
11595

116-
if (templateVariableName) {
117-
variableName = makeNameByTemplate(value, prop);
118-
result = variableName;
96+
function makeCSSVariable(templateVariableName, variablePrefix, variableSyntax, onlyColor,
97+
variablesListCounter, prop, num, value) {
98+
let variableName = '';
99+
let result = '';
119100

120-
if (!variablesListCounter[variableName]) {
121-
variablesListCounter[variableName] = 1;
122-
}
101+
if (templateVariableName) {
102+
variableName = makeNameByTemplate(templateVariableName, onlyColor, value, prop);
103+
result = variableName;
123104

124-
result += `-${variablesListCounter[variableName]}`;
125-
variablesListCounter[variableName] += 1;
126-
} else {
127-
variableName = `${prop}-${num}`;
128-
result = variableName;
105+
if (!variablesListCounter[variableName]) {
106+
variablesListCounter[variableName] = 1;
129107
}
130108

131-
return `--${result}`;
109+
result += `-${variablesListCounter[variableName]}`;
110+
variablesListCounter[variableName] += 1;
111+
} else {
112+
variableName = `${prop}-${num}`;
113+
result = variableName;
132114
}
133115

134-
function addCSSVariable(currentScope, value, variableName) {
116+
return addVariablePrefix(variablePrefix, variableSyntax, result);
117+
}
118+
119+
function addCSSVariable(variableSyntax, currentScope, value, variableName) {
120+
if (variableSyntax) {
121+
currentScope.prepend(`${variableName}: ${value}`);
122+
} else {
135123
currentScope.append(`${variableName}: ${value}`);
136124
}
125+
}
126+
127+
function hasVariable(variableSyntax, reCSSVariable, value) {
128+
const reTest = reCSSVariable[variableSyntax] || reCSSVariable.default;
129+
return reTest.test(value);
130+
}
131+
132+
module.exports = postcss.plugin('postcss-extract-value', (opts) => {
133+
// Fix for Node 4
134+
const params = opts || {};
135+
136+
// Options
137+
const filterByProps = params.filterByProps;
138+
const onlyColor = params.onlyColor;
139+
const scope = params.scope || ':root';
140+
const templateVariableName = params.templateVariableName || '';
141+
const variableSyntax = params.variableSyntax || '';
142+
143+
144+
const variablePrefix = {
145+
default: '--',
146+
less: '@',
147+
sass: '$',
148+
};
149+
150+
const variablesListCounter = {};
151+
152+
// Cache RegExp
153+
const reColorKeywords = new RegExp(colorNameList.join('|'));
154+
const reCSSVariable = {
155+
default: /^var\(-{2}\w{1}[\w+-]*/,
156+
sass: /\$\w{1}[\w+-]*/,
157+
less: /@\w{1}[\w+-]*/,
158+
};
159+
const reHex = /#(\w{6}|\w{3})/;
160+
const reRgb = /rgba?\([\d,.\s]+\)/;
161+
const reHsl = /hsla?\(\s?[0-9]{1,3},\s?([0-9]{1,3}%,?\s?){2}([0-9.]+)?\)/;
162+
const reExtract = new RegExp(`${reHex.source}|${reRgb.source}|${reHsl.source}|${reColorKeywords.source}`, 'g');
137163

138164
return function parser(css) {
139165
const root = css.root();
@@ -151,8 +177,9 @@ module.exports = postcss.plugin('postcss-extract-value', (opts) => {
151177
rootSel = rule;
152178
} else {
153179
rule.walkDecls((decl) => {
154-
if (!reCSSVariable.test(decl.value)) {
155-
checkColorFilter = !onlyColor || onlyColor && isColor(decl.value);
180+
if (!hasVariable(variableSyntax, reCSSVariable, decl.value)) {
181+
checkColorFilter = !onlyColor || onlyColor
182+
&& isColor(reColorKeywords, decl.value);
156183

157184
checkPropFilter = (!filterByProps || filterByProps
158185
&& checkProp(filterByProps, decl.prop));
@@ -163,7 +190,7 @@ module.exports = postcss.plugin('postcss-extract-value', (opts) => {
163190
}
164191

165192
if (onlyColor) {
166-
filteredValueList = extractColor(decl.value);
193+
filteredValueList = extractColor(reExtract, decl.value);
167194
} else {
168195
filteredValueList = new Array(decl.value);
169196
}
@@ -178,11 +205,18 @@ module.exports = postcss.plugin('postcss-extract-value', (opts) => {
178205
variableName = variablesList[filteredValue];
179206
} else {
180207
positionValue = storeProps[decl.prop].indexOf(filteredValue) + 1;
181-
variableName = makeCSSVariable(decl.prop, positionValue, filteredValue);
208+
variableName = makeCSSVariable(templateVariableName, variablePrefix,
209+
variableSyntax, onlyColor, variablesListCounter, decl.prop, positionValue,
210+
filteredValue);
182211
variablesList[filteredValue] = variableName;
183212
}
184-
decl.value = decl.value.replace(filteredValue,
185-
`var(${variableName})`);
213+
if (variableSyntax) {
214+
decl.value = decl.value.replace(filteredValue,
215+
`${variableName}`);
216+
} else {
217+
decl.value = decl.value.replace(filteredValue,
218+
`var(${variableName})`);
219+
}
186220
});
187221
}
188222
}
@@ -191,12 +225,21 @@ module.exports = postcss.plugin('postcss-extract-value', (opts) => {
191225
});
192226

193227
if (Object.keys(rootSel).length === 0) {
194-
rootSel = postcss.rule({ selector: scope });
195-
root.prepend(rootSel);
228+
if (!variableSyntax) {
229+
rootSel = postcss.rule({ selector: scope });
230+
root.prepend(rootSel);
231+
} else {
232+
rootSel = root;
233+
}
234+
}
235+
236+
const varialbleListKeys = Object.keys(variablesList);
237+
if (variableSyntax) {
238+
varialbleListKeys.reverse();
196239
}
197240

198-
Object.keys(variablesList).forEach((value) => {
199-
addCSSVariable(rootSel, value, variablesList[value]);
241+
varialbleListKeys.forEach((value) => {
242+
addCSSVariable(variableSyntax, rootSel, value, variablesList[value]);
200243
});
201244
};
202245
});

0 commit comments

Comments
 (0)