Skip to content

Commit 7827c7c

Browse files
committed
various improvements
1 parent 8eb32cf commit 7827c7c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+787
-20
lines changed

lib/is-node-under-scope.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
var isUnderScope = require('./is-under-scope');
22
var generateScopeList = require('./generate-scope-list');
33

4-
var isNodeUnderScope = function(node, scopeNode, /*optional*/ignorePseudo) {
4+
var isNodeUnderScope = function(node, scopeNode, /*optional*/ignorePseudo, /*optional*/keepPseudo) {
55
var nodeScopeList = generateScopeList(node, true);
66
var scopeNodeScopeList = generateScopeList(scopeNode, true);
77

8-
return isUnderScope(nodeScopeList, scopeNodeScopeList, ignorePseudo);
8+
return isUnderScope(nodeScopeList, scopeNodeScopeList, ignorePseudo, keepPseudo);
99
};
1010

1111
module.exports = isNodeUnderScope;

lib/is-under-scope.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,13 @@ var stripPseudoSelectorsFromScopeList = function(scopeList) {
133133
// Another way to think about it: Can the target scope cascade properties to the node?
134134
//
135135
// For scope-lists see: `generateScopeList`
136-
var isUnderScope = function(nodeScopeList, scopeNodeScopeList, /*optional*/ignorePseudo) {
137-
// Because we only care about the scopeNodeScope matching to the nodeScope
138-
// Remove the pseudo selectors from the nodeScope so it can match a broader version
139-
// ex. `.foo:hover` can resolve variables from `.foo`
140-
nodeScopeList = stripPseudoSelectorsFromScopeList(nodeScopeList);
136+
var isUnderScope = function(nodeScopeList, scopeNodeScopeList, /*optional*/ignorePseudo, /*optional*/keepPseudo) {
137+
if(!keepPseudo) {
138+
// Because we only care about the scopeNodeScope matching to the nodeScope
139+
// Remove the pseudo selectors from the nodeScope so it can match a broader version
140+
// ex. `.foo:hover` can resolve variables from `.foo`
141+
nodeScopeList = stripPseudoSelectorsFromScopeList(nodeScopeList);
142+
}
141143

142144
if(ignorePseudo) {
143145
scopeNodeScopeList = stripPseudoSelectorsFromScopeList(scopeNodeScopeList);

lib/resolve-decl.js

Lines changed: 155 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2+
var log = require('debug')('postcss-css-variables:resolve-decl');
3+
14
var resolveValue = require('./resolve-value');
25
var generateScopeList = require('./generate-scope-list');
36
var gatherVariableDependencies = require('./gather-variable-dependencies');
@@ -9,15 +12,85 @@ var shallowCloneNode = require('./shallow-clone-node');
912
var findNodeAncestorWithSelector = require('./find-node-ancestor-with-selector');
1013
var cloneSpliceParentOntoNodeWhen = require('./clone-splice-parent-onto-node-when');
1114

15+
var parser = require('postcss-selector-parser');
16+
17+
function equals(a, b) {
18+
return a && b && a.type !== undefined && a.value !== undefined && a.type === b.type && a.value === b.value;
19+
}
20+
21+
function compatible(a, b) {
22+
var af = a.filter(c => !b.find(d => equals(d, c)));
23+
var bf = b.filter(c => !a.find(d => equals(d, c)));
24+
var ac = a.find(c => c.type == 'combinator');
25+
var bc = b.find(c => c.type == 'combinator');
26+
27+
if(bf.length == 0 && ((!ac && !bc) || equals(ac, bc)))
28+
{
29+
return true;
30+
}
31+
32+
return false;
33+
}
34+
35+
function compatible2(a, b) {
36+
var af = a.filter(c => !b.find(d => equals(d, c)));
37+
var bf = b.filter(c => !a.find(d => equals(d, c)));
38+
39+
if(bf.length == 0 && af.length != 0)
40+
{
41+
return true;
42+
}
43+
44+
return false;
45+
}
46+
47+
function isSpace(a) {
48+
return a && a.type === 'combinator' && a.value.trim() === '';
49+
}
50+
51+
function findIndex(s, fn, start)
52+
{
53+
if(!start)
54+
{
55+
start = 0;
56+
}
57+
else
58+
{
59+
s = s.slice(start);
60+
}
61+
62+
var pos = s.findIndex(fn);
63+
64+
return pos === -1 ? -1 : s.findIndex(fn) + start;
65+
}
66+
67+
function parseSelector(selector) {
68+
var ret;
69+
70+
parser(selectors => {
71+
ret = selectors.first.split(selector => selector.type === 'combinator').map(a => a.filter(b => !isSpace(b)));
72+
}).processSync(selector);
1273

74+
return ret;
75+
}
1376

1477
function eachMapItemDependencyOfDecl(variablesUsedList, map, decl, cb) {
78+
log('>>>>>>>>> DECL ' + decl.parent.selector + ' / ' + decl.prop + ' = ' + decl.value);
79+
80+
var declSelector = decl.parent.selector ? parseSelector(decl.parent) : null;
81+
82+
log(declSelector);
83+
1584
// Now find any at-rule declarations that pertains to each rule
1685
// Loop through the variables used
1786
variablesUsedList.forEach(function(variableUsedName) {
1887

88+
log('>>>>>> VAR '+variableUsedName);
89+
1990
// Find anything in the map that corresponds to that variable
20-
gatherVariableDependencies(variablesUsedList, map).deps.forEach(function(mapItem) {
91+
gatherVariableDependencies(variablesUsedList, map).deps.forEach(function(mapItem, i) {
92+
93+
log('>>> DEP '+i+' / ' + mapItem.parent.selector + ' / ' + mapItem.prop + ' = ' + mapItem.calculatedInPlaceValue);
2194

2295
var mimicDecl;
2396
if(mapItem.isUnderAtRule) {
@@ -41,29 +114,103 @@ function eachMapItemDependencyOfDecl(variablesUsedList, map, decl, cb) {
41114
//console.log(generateScopeList(mapItem.parent, true));
42115
//console.log('amd isNodeUnderScope', isNodeUnderScope(mimicDecl.parent, mapItem.parent), mapItem.decl.value);
43116
}
44-
// TODO: use regex from `isUnderScope`
45-
else if(isUnderScope.RE_PSEUDO_SELECTOR.test(mapItem.parent.selector)) {
117+
// // TODO: use regex from `isUnderScope`
118+
// else if(isUnderScope.RE_PSEUDO_SELECTOR.test(mapItem.parent.selector)) {
119+
// // Create a detached clone
120+
// var ruleClone = shallowCloneNode(decl.parent);
121+
// ruleClone.parent = decl.parent.parent;
122+
//
123+
// // Add the declaration to it
124+
// mimicDecl = decl.clone();
125+
// ruleClone.append(mimicDecl);
126+
//
127+
// var lastPseudoSelectorMatches = mapItem.parent.selector.match(new RegExp(isUnderScope.RE_PSEUDO_SELECTOR.source + '$'));
128+
// var lastPseudoSelector = lastPseudoSelectorMatches ? lastPseudoSelectorMatches[2] : '';
129+
//
130+
// ruleClone.selector += lastPseudoSelector;
131+
// }
132+
else if(mapItem.parent.selector !== decl.parent.selector && declSelector) {
133+
var s = parseSelector(mapItem.parent);
134+
135+
log(s);
136+
137+
var append = null;
138+
var idx = 0;
139+
var process = false;
140+
for(var i = 0; i < declSelector.length; i++) {
141+
var a = declSelector[i];
142+
var b = findIndex(s, c => compatible(c, a), idx);
143+
process |= s.findIndex(c => compatible2(c, a)) != -1;
144+
145+
if(b < idx) {
146+
// already compatible
147+
if(i === 0) {
148+
log('i === 0', b, idx);
149+
return;
150+
}
151+
// invalid unless last item
152+
else if(i != declSelector.length-1) {
153+
log('i != declSelector.length-1', b, idx);
154+
return;
155+
}
156+
157+
log('append', a);
158+
append = a;
159+
}
160+
else {
161+
idx = b;
162+
}
163+
}
164+
165+
// #foo li:hover a @ compound16.css
166+
if(!append && idx != s.length-1) {
167+
log('idx != s.length-1', idx, s.length-1);
168+
return;
169+
}
170+
171+
// already compatible
172+
if(!process) {
173+
log('!process');
174+
return;
175+
}
176+
46177
// Create a detached clone
47178
var ruleClone = shallowCloneNode(decl.parent);
48179
ruleClone.parent = decl.parent.parent;
49180

50181
// Add the declaration to it
51182
mimicDecl = decl.clone();
52-
ruleClone.append(mimicDecl);
53183

54-
var lastPseudoSelectorMatches = mapItem.parent.selector.match(new RegExp(isUnderScope.RE_PSEUDO_SELECTOR.source + '$'));
55-
var lastPseudoSelector = lastPseudoSelectorMatches ? lastPseudoSelectorMatches[2] : '';
184+
// FIXME? short-circuit to fix pseudo selectors
185+
mimicDecl.value = mapItem.calculatedInPlaceValue;
186+
187+
ruleClone.append(mimicDecl);
188+
ruleClone.selector = mapItem.parent.selector;
56189

57-
ruleClone.selector += lastPseudoSelector;
190+
// append last element of selector where needed
191+
if(append) {
192+
ruleClone.selector += ' ';
193+
append.forEach(a => ruleClone.selector += String(a));
194+
}
58195
}
59196

60197
// If it is under the proper scope,
61198
// we need to check because we are iterating over all map entries
62-
if(mimicDecl && isNodeUnderScope(mimicDecl, mapItem.parent, true)) {
199+
var valid = mimicDecl && isNodeUnderScope(mimicDecl, mapItem.parent, true);
200+
201+
if(valid) {
63202
cb(mimicDecl, mapItem);
64203
}
204+
205+
log('SELECTOR '+(mimicDecl ? mimicDecl.parent.selector : null));
206+
log('VALID? '+valid);
207+
log('<<< DEP '+i+' / ' + mapItem.parent.selector + ' / ' + mapItem.prop + ' = ' + mapItem.calculatedInPlaceValue);
65208
});
209+
210+
log('<<<<<< VAR '+variableUsedName);
66211
});
212+
213+
log('<<<<<<<<< DECL ' + decl.parent.selector + ' / ' + decl.prop + ' = ' + decl.value);
67214
}
68215

69216

lib/resolve-value.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2+
var log = require('debug')('postcss-css-variables:resolve-value');
3+
14
var balanced = require('balanced-match');
25

36
var generateScopeList = require('./generate-scope-list');
@@ -64,6 +67,8 @@ function balancedVar(value) {
6467
var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal debugging*/_debugIsInternal) {
6568
var debugIndent = _debugIsInternal ? '\t' : '';
6669

70+
// log(decl, map, ignorePseudoScope);
71+
6772
var matchingVarDecl = undefined;
6873
var resultantValue = toString(decl.value);
6974
var warnings = [];
@@ -117,7 +122,9 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
117122
var isRoot = varDeclMapItem.parent.type === 'root' || varDeclMapItem.parent.selectors[0] === ':root';
118123

119124
var underScope = isNodeUnderScope(decl.parent, varDeclMapItem.parent);
120-
var underScsopeIgnorePseudo = isNodeUnderScope(decl.parent, varDeclMapItem.parent, ignorePseudoScope);
125+
var underScsopeIgnorePseudo = isNodeUnderScope(decl.parent, varDeclMapItem.parent, ignorePseudoScope, !!matchingVarDeclMapItem);
126+
127+
// log("###", variableName, underScope, underScsopeIgnorePseudo, matchingVarDeclMapItem && matchingVarDeclMapItem.decl.value, /*matchingVarDeclMapItem,*/ decl.parent, varDeclMapItem.parent);
121128

122129
//console.log(debugIndent, 'isNodeUnderScope', underScope, underScsopeIgnorePseudo, generateScopeList(varDeclMapItem.parent, true), varDeclMapItem.decl.value);
123130

@@ -141,6 +148,7 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
141148

142149
return fallbackValue;
143150
})();
151+
144152
// Otherwise if the dependency health is good(no circular or self references), dive deeper and resolve
145153
if(matchingVarDeclMapItem !== undefined && !gatherVariableDependencies(variablesUsedInValue, map).hasCircularOrSelfReference) {
146154
// Splice the declaration parent onto the matching entry
@@ -165,6 +173,8 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal
165173
resultantValue = (matchingVarDecl.pre || '') + replaceValue + (matchingVarDecl.post || '')
166174
}
167175

176+
log("RESULT:", !isResultantValueUndefined ? resultantValue : undefined);
177+
168178
return {
169179
// The resolved value
170180
value: !isResultantValueUndefined ? resultantValue : undefined,

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"balanced-match": "^1.0.0",
1818
"escape-string-regexp": "^1.0.3",
1919
"extend": "^3.0.1",
20-
"postcss": "^6.0.8"
20+
"postcss": "^6.0.8",
21+
"postcss-selector-parser": "^6.0.2"
2122
},
2223
"devDependencies": {
2324
"bluebird": "^3.5.0",
@@ -28,7 +29,8 @@
2829
"eslint-plugin-react": "^7.1.0",
2930
"mocha": "^5.2.0",
3031
"postcss-discard-comments": "^4.0.0",
31-
"postcss-normalize-whitespace": "^4.0.0"
32+
"postcss-normalize-whitespace": "^4.0.0",
33+
"debug": "*"
3234
},
3335
"scripts": {
3436
"test": "mocha",

test/fixtures/compound1.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
.foo {
3+
--color: #ffffff;
4+
color: var(--color);
5+
}
6+
7+
.foo.bar {
8+
--color: #000000;
9+
}

test/fixtures/compound1.expected.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
.foo {
3+
color: #ffffff;
4+
}
5+
6+
.foo.bar {
7+
color: #000000;
8+
}

test/fixtures/compound10.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
#foo.bar li {
3+
--color: #ffffff;
4+
color: var(--color);
5+
}
6+
7+
#foo li {
8+
--color: #000000;
9+
}

test/fixtures/compound10.expected.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
#foo.bar li {
3+
color: #ffffff;
4+
}

test/fixtures/compound11.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
#foo a li {
3+
--color: #ffffff;
4+
color: var(--color);
5+
}
6+
7+
#foo.bar a + li {
8+
--color: #000000;
9+
}
10+
11+
#bar a + li {
12+
--color2: #ffffff;
13+
color: var(--color2);
14+
}
15+
16+
#bar.bar a + li {
17+
--color2: #000000;
18+
}

test/fixtures/compound11.expected.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
#foo a li {
3+
color: #ffffff;
4+
}
5+
6+
#bar a + li {
7+
color: #ffffff;
8+
}
9+
10+
#bar.bar a + li {
11+
color: #000000;
12+
}

test/fixtures/compound12.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
#foo a b + c d li {
3+
--color: #ffffff;
4+
color: var(--color);
5+
}
6+
7+
#foo.bar a c b + c d e {
8+
--color: #000000;
9+
}
10+
11+
#bar a b + c d li {
12+
--color2: #ffffff;
13+
color: var(--color2);
14+
}
15+
16+
#bar.bar a c b c + d e {
17+
--color: #000000;
18+
}

test/fixtures/compound12.expected.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
#foo a b + c d li {
3+
color: #ffffff;
4+
}
5+
6+
#foo.bar a c b + c d e li {
7+
color: #000000;
8+
}
9+
10+
#bar a b + c d li {
11+
color: #ffffff;
12+
}

0 commit comments

Comments
 (0)