postcss-css-variables
Advanced tools
Comparing version
@@ -0,6 +1,9 @@ | ||
# v0.3.8 - 2015-5-28 | ||
- Add support for pseudo selectors `:hover` `:before` | ||
# v0.3.7 - 2015-5-27 | ||
- Fix #7: Support for child combinator | ||
- Added tests for child combinator coverage | ||
- Added tests for child-combinator/direct-descendant coverage | ||
@@ -7,0 +10,0 @@ # v0.3.6 - 2015-5-21 |
26
index.js
// PostCSS CSS Variables (postcss-css-variables) | ||
// v0.3.7 | ||
// v0.3.8 | ||
// | ||
@@ -49,14 +49,14 @@ // https://github.com/MadLittleMods/postcss-css-variables | ||
var defaults = { | ||
// Allows you to preserve custom properties & var() usage in output. | ||
// `true`, `false`, or `'computed'` | ||
preserve: false, | ||
// Define variables via JS | ||
// Simple key-value pair | ||
// or an object with a `value` property and an optional `isImportant` bool property | ||
variables: {} | ||
}; | ||
module.exports = postcss.plugin('postcss-css-variables', function(options) { | ||
var defaults = { | ||
// Allows you to preserve custom properties & var() usage in output. | ||
// `true`, `false`, or `'computed'` | ||
preserve: false, | ||
// Define variables via JS | ||
// Simple key-value pair | ||
// or an object with a `value` property and an optional `isImportant` bool property | ||
variables: {} | ||
}; | ||
opts = extend({}, defaults, options); | ||
@@ -69,3 +69,3 @@ | ||
/* */ | ||
/* * / | ||
try { | ||
@@ -248,3 +248,3 @@ /* */ | ||
/* */ | ||
/* * / | ||
} | ||
@@ -251,0 +251,0 @@ catch(e) { |
@@ -19,5 +19,3 @@ // Unit Tests: https://regex101.com/r/oP0fM9/15 | ||
// and trim off the CSS4 descendant `>>` into a normal descendant selector | ||
return piece.trim().replace(/\s*?>>\s*?/, function(match) { | ||
return ''; | ||
}); | ||
return piece.trim().replace(/\s*?>>\s*?/g, ''); | ||
}); | ||
@@ -24,0 +22,0 @@ }; |
@@ -17,5 +17,3 @@ // Unit Tests: https://regex101.com/r/oS4zJ8/3 | ||
// and trim off the CSS4 descendant `>>` into a normal descendant selector | ||
return piece.trim().replace(/\s*?>\s*?/, function(match) { | ||
return ''; | ||
}); | ||
return piece.trim().replace(/\s*?>\s*?/g, ''); | ||
}); | ||
@@ -22,0 +20,0 @@ }; |
var isUnderScope = require('./is-under-scope'); | ||
var generateScopeList = require('./generate-scope-list'); | ||
var isNodeUnderScope = function(node, scopeNode) { | ||
var isNodeUnderScope = function(node, scopeNode, /*optional*/ignorePseudo) { | ||
var nodeScopeList = generateScopeList(node, true); | ||
var scopeNodeScopeList = generateScopeList(scopeNode, true); | ||
return isUnderScope(nodeScopeList, scopeNodeScopeList); | ||
return isUnderScope(nodeScopeList, scopeNodeScopeList, ignorePseudo); | ||
}; | ||
module.exports = isNodeUnderScope; |
@@ -6,5 +6,10 @@ var escapeStringRegexp = require('escape-string-regexp'); | ||
var RE_AT_RULE_SCOPE_PIECE = (/^@.*/); | ||
// This will match pseudo selectors that have a base part | ||
// ex. .foo:hover | ||
// It will NOT match `:root` | ||
var RE_PSEUDO_SELECTOR = (/([^\s:]+)((?::|::)[^\s]*?)(\s+|$)/); | ||
function asdfqwer(nodeScopeList, scopeNodeScopeList) { | ||
function getScopeMatchResults(nodeScopeList, scopeNodeScopeList) { | ||
var currentPieceOffset; | ||
@@ -17,6 +22,7 @@ var scopePieceIndex; | ||
//console.log('sp', scopeNodeScopePieces); | ||
//console.log('np', nodeScopePieces); | ||
currentPieceOffset = null; | ||
var wasEveryPieceFound = true; | ||
// scopeNodeScopePieces.every(function(scopePiece) { | ||
for(scopePieceIndex = 0; scopePieceIndex < scopeNodeScopePieces.length; scopePieceIndex++) { | ||
@@ -29,3 +35,2 @@ var scopePiece = scopeNodeScopePieces[scopePieceIndex]; | ||
var piecesWeCanMatch = nodeScopePieces.slice(pieceOffset); | ||
//piecesWeCanMatch.some(function(nodeScopePiece, index) { | ||
for(var nodeScopePieceIndex = 0; nodeScopePieceIndex < piecesWeCanMatch.length; nodeScopePieceIndex++) { | ||
@@ -61,5 +66,7 @@ var nodeScopePiece = piecesWeCanMatch[nodeScopePieceIndex]; | ||
var directDescendantPieces = generateDirectDescendantPiecesFromSelector(nodeScopePiece); | ||
// Only try to work out direct descendants if there was the `>` combinator, meaning multiple pieces | ||
if(directDescendantPieces.length > 1) { | ||
var ddNodeScopeList = [].concat([directDescendantPieces]); | ||
// Massage into a direct descendant separated list | ||
var ddScopeList = [].concat([ | ||
@@ -72,3 +79,3 @@ scopeNodeScopePieces | ||
]); | ||
var result = asdfqwer(ddNodeScopeList, ddScopeList); | ||
var result = getScopeMatchResults(ddNodeScopeList, ddScopeList); | ||
@@ -79,3 +86,4 @@ // If it matches completely | ||
foundIndex = overallIndex; | ||
// -1 because the fo loop increments at the top | ||
// Move the scope forward the amount that piece consumed | ||
// -1 because the of for-loop increments at each iteration | ||
scopePieceIndex += result.scopePieceIndex-1; | ||
@@ -86,7 +94,2 @@ } | ||
} | ||
if(directDescendantPieces.length > 1) { | ||
var asdf = scopeNodeScopePieces.slice(scopePieceIndex); | ||
} | ||
} | ||
@@ -99,2 +102,3 @@ | ||
// Mimicing a `[].every` with a for-loop | ||
wasEveryPieceFound = wasEveryPieceFound && isFurther; | ||
@@ -119,2 +123,15 @@ if(!wasEveryPieceFound) { | ||
var stripPseudoSelectorsFromScopeList = function(scopeList) { | ||
return scopeList.map(function(scopePieces) { | ||
return scopePieces.map(function(descendantPiece) { | ||
// If not an at-rule piece, remove the pseudo selector part `@media (max-width: 300px)` | ||
if(!RE_AT_RULE_SCOPE_PIECE.test(descendantPiece)) { | ||
return descendantPiece.replace(new RegExp(RE_PSEUDO_SELECTOR.source, 'g'), function(whole, baseSelector, pseudo, trailingWhitespace) { | ||
return baseSelector + trailingWhitespace; | ||
}); | ||
} | ||
return descendantPiece; | ||
}); | ||
}); | ||
}; | ||
@@ -128,6 +145,17 @@ | ||
// For scope-lists see: `generateScopeList` | ||
var isUnderScope = function isUnderScope(nodeScopeList, scopeNodeScopeList) { | ||
return asdfqwer(nodeScopeList, scopeNodeScopeList).doesMatchScope; | ||
var isUnderScope = function(nodeScopeList, scopeNodeScopeList, /*optional*/ignorePseudo) { | ||
// Because we only care about the scopeNodeScope matching to the nodeScope | ||
// Remove the pseudo selectors from the nodeScope so it can match a broader version | ||
// ex. `.foo:hover` can resolve variables from `.foo` | ||
nodeScopeList = stripPseudoSelectorsFromScopeList(nodeScopeList); | ||
if(ignorePseudo) { | ||
scopeNodeScopeList = stripPseudoSelectorsFromScopeList(scopeNodeScopeList); | ||
} | ||
return getScopeMatchResults(nodeScopeList, scopeNodeScopeList).doesMatchScope; | ||
}; | ||
isUnderScope.RE_PSEUDO_SELECTOR = RE_PSEUDO_SELECTOR; | ||
module.exports = isUnderScope; |
var resolveValue = require('./resolve-value'); | ||
var generateScopeList = require('./generate-scope-list'); | ||
var gatherVariableDependencies = require('./gather-variable-dependencies'); | ||
var isUnderScope = require('./is-under-scope'); | ||
var isNodeUnderScope = require('./is-node-under-scope'); | ||
@@ -27,36 +29,46 @@ | ||
// Resolve the cascade | ||
// Resolve the cascade dependencies | ||
// Now find any at-rule declarations that need to be added below each rule | ||
eachMapItemUnderAtRuleUsedByVariable(valueResults.variablesUsed, map, decl, function(mimicDecl, mapItem) { | ||
// Create the clean atRule for which we place the declaration under | ||
var atRuleNode = mapItem.parent.parent.clone().removeAll(); | ||
eachMapItemDependencyOfDecl(valueResults.variablesUsed, map, decl, function(mimicDecl, mapItem) { | ||
var ruleClone = decl.parent.clone().removeAll(); | ||
var declClone = decl.clone(); | ||
declClone.value = _logResolveValueResult(resolveValue(mimicDecl, map)).value; | ||
// No mangle resolve | ||
declClone.value = _logResolveValueResult(resolveValue(mimicDecl, map, true)).value; | ||
// Add the declaration to our new rule | ||
ruleClone.append(declClone); | ||
// Add the rule to the atRule | ||
atRuleNode.append(ruleClone); | ||
// Since that atRuleNode can be nested in other atRules, we need to make the appropriate structure | ||
var parentAtRuleNode = atRuleNode; | ||
var currentAtRuleNode = mapItem.parent.parent; | ||
while(currentAtRuleNode.parent.type === 'atrule') { | ||
// Create a new clean clone of that at rule to nest under | ||
var newParentAtRuleNode = currentAtRuleNode.parent.clone().removeAll(); | ||
if(mapItem.isUnderAtRule) { | ||
// Create the clean atRule for which we place the declaration under | ||
var atRuleNode = mapItem.parent.parent.clone().removeAll(); | ||
// Append the old parent | ||
newParentAtRuleNode.append(parentAtRuleNode); | ||
// Then set the new one as the current for next iteration | ||
parentAtRuleNode = newParentAtRuleNode; | ||
// Add the rule to the atRule | ||
atRuleNode.append(ruleClone); | ||
currentAtRuleNode = currentAtRuleNode.parent; | ||
// Since that atRuleNode can be nested in other atRules, we need to make the appropriate structure | ||
var parentAtRuleNode = atRuleNode; | ||
var currentAtRuleNode = mapItem.parent.parent; | ||
while(currentAtRuleNode.parent.type === 'atrule') { | ||
// Create a new clean clone of that at rule to nest under | ||
var newParentAtRuleNode = currentAtRuleNode.parent.clone().removeAll(); | ||
// Append the old parent | ||
newParentAtRuleNode.append(parentAtRuleNode); | ||
// Then set the new one as the current for next iteration | ||
parentAtRuleNode = newParentAtRuleNode; | ||
currentAtRuleNode = currentAtRuleNode.parent; | ||
} | ||
// Put the atRuleStructure after the declaration's rule | ||
decl.parent.parent.insertAfter(decl.parent, parentAtRuleNode); | ||
} | ||
else { | ||
ruleClone.selector = mimicDecl.parent.selector; | ||
// Put the atRuleStructure after the declaration's rule | ||
decl.parent.parent.insertAfter(decl.parent, parentAtRuleNode); | ||
// Put the atRuleStructure after the declaration's rule | ||
decl.parent.parent.insertAfter(decl.parent, ruleClone); | ||
} | ||
}); | ||
@@ -76,3 +88,3 @@ | ||
function eachMapItemUnderAtRuleUsedByVariable(variablesUsedList, map, decl, cb) { | ||
function eachMapItemDependencyOfDecl(variablesUsedList, map, decl, cb) { | ||
// Now find any at-rule declarations that pertains to each rule | ||
@@ -84,2 +96,4 @@ // Loop through the variables used | ||
gatherVariableDependencies(variablesUsedList, map).deps.forEach(function(mapItem) { | ||
var mimicDecl; | ||
if(mapItem.isUnderAtRule) { | ||
@@ -96,3 +110,3 @@ | ||
var varDeclAtRule = mapItem.parent.parent; | ||
var mimicDecl = cloneSpliceParentOntoNodeWhen(decl, varDeclAtRule, function(ancestor) { | ||
mimicDecl = cloneSpliceParentOntoNodeWhen(decl, varDeclAtRule, function(ancestor) { | ||
return ancestor === nodeToSpliceParentOnto; | ||
@@ -106,10 +120,24 @@ }); | ||
//console.log('amd isNodeUnderScope', isNodeUnderScope(mimicDecl.parent, mapItem.parent), mapItem.decl.value); | ||
} | ||
// TODO: use regex from `isUnderScope` | ||
else if(isUnderScope.RE_PSEUDO_SELECTOR.test(mapItem.parent.selector)) { | ||
// Create a detached clone | ||
var ruleClone = decl.parent.clone().removeAll(); | ||
ruleClone.parent = decl.parent.parent; | ||
// If it is under the proper scope, | ||
// we need to check because we are iterating over all map entries that are `isUnderAtRule` | ||
// Then lets create the new rules | ||
if(isNodeUnderScope(mimicDecl.parent, mapItem.parent)) { | ||
cb(mimicDecl, mapItem); | ||
} | ||
// Add the declaration to it | ||
mimicDecl = decl.clone(); | ||
ruleClone.append(mimicDecl); | ||
var lastPseudoSelectorMatches = mapItem.parent.selector.match(new RegExp(isUnderScope.RE_PSEUDO_SELECTOR.source + '$')); | ||
var lastPseudoSelector = lastPseudoSelectorMatches ? lastPseudoSelectorMatches[2] : ''; | ||
ruleClone.selector += lastPseudoSelector; | ||
} | ||
// If it is under the proper scope, | ||
// we need to check because we are iterating over all map entries | ||
if(mimicDecl && isNodeUnderScope(mimicDecl, mapItem.parent, true)) { | ||
cb(mimicDecl, mapItem); | ||
} | ||
}); | ||
@@ -116,0 +144,0 @@ }); |
@@ -17,2 +17,4 @@ var generateScopeList = require('./generate-scope-list'); | ||
// and we can figure out the final value | ||
// | ||
// `ignorePseudoScope`: Optional bool to determine whether the scope resolution should be left alone or not | ||
// | ||
@@ -22,3 +24,3 @@ // Note: We do not modify the declaration | ||
// This means, feel free to run everything through this function | ||
var resolveValue = function(decl, map, /*internal debugging*/_debugIsInternal) { | ||
var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal debugging*/_debugIsInternal) { | ||
@@ -56,3 +58,3 @@ var resultantValue = decl.value; | ||
if( | ||
isNodeUnderScope(decl.parent, varDeclMapItem.parent) && | ||
isNodeUnderScope(decl.parent, varDeclMapItem.parent, ignorePseudoScope) && | ||
// And if the currently matched declaration is `!important`, it will take another `!important` to override it | ||
@@ -79,3 +81,3 @@ (!(matchingVarDeclMapItem || {}).isImportant || varDeclMapItem.isImportant) | ||
replaceValue = resolveValue(matchingMimicDecl, map, true).value; | ||
replaceValue = resolveValue(matchingMimicDecl, map, /*internal*/true).value; | ||
} | ||
@@ -82,0 +84,0 @@ |
{ | ||
"name": "postcss-css-variables", | ||
"version": "0.3.7", | ||
"version": "0.3.8", | ||
"description": "PostCSS plugin to transform CSS Custom Properties(CSS variables) syntax into a static representation", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -11,3 +11,3 @@ [](http://badge.fury.io/js/postcss-css-variables) [](https://travis-ci.org/MadLittleMods/postcss-css-variables) | ||
## Latest Version: v0.3.7 | ||
## Latest Version: v0.3.8 | ||
### [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) | ||
@@ -86,3 +86,29 @@ | ||
## Pseudo class and elements | ||
```css | ||
.foo { | ||
--foo-color: #ff0000; | ||
color: var(--foo-color); | ||
} | ||
.foo:hover { | ||
--foo-color: #00ff00; | ||
} | ||
``` | ||
will be processed to: | ||
```css | ||
.foo { | ||
color: #ff0000; | ||
} | ||
.foo:hover { | ||
color: #00ff00; | ||
} | ||
``` | ||
## Nested rules | ||
@@ -89,0 +115,0 @@ |
45851
6.49%723
6.79%339
8.31%