From b1a6ef23f820217bb90970e179b84783df8732fd Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Mon, 29 Jun 2015 02:17:23 -0500 Subject: [PATCH] Remove `opts` global leak. Fix #13 - v0.3.9 --- CHANGELOG.md | 6 + README.md | 6 +- index.js | 14 +- lib/clone-splice-parent-onto-node-when.js | 10 +- lib/find-node-ancestor-with-selector.js | 5 +- lib/gather-variable-dependencies.js | 2 +- ...enerate-descendant-pieces-from-selector.js | 2 +- ...-direct-descendant-pieces-from-selector.js | 2 +- lib/generate-scope-list.js | 6 +- lib/is-node-under-scope.js | 2 +- lib/is-piece-always-ancestor-selector.js | 2 +- lib/is-under-scope.js | 12 +- lib/resolve-decl.js | 125 +++++++++--------- lib/resolve-value.js | 10 +- package.json | 2 +- 15 files changed, 109 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a7164a..b438a5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ + +# v0.3.9 - 2015-6-29 + + - Remove `opts` global leak. Fix #13 + + # v0.3.8 - 2015-5-28 - Add support for pseudo selectors `:hover` `:before` diff --git a/README.md b/README.md index f3ce29e..375289a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ CSS variables or CSS Custom Properties limited subset polyfill/shim. We strive for the most complete transformation but we/no plugin can achieve true complete parity according to the [specification](http://dev.w3.org/csswg/css-variables/) because of the DOM cascade unknowns. -## Latest Version: v0.3.8 +## Latest Version: v0.3.9 ### [Changelog](https://github.com/MadLittleMods/postcss-css-variables/blob/master/CHANGELOG.md) ### Install @@ -217,7 +217,7 @@ will be processed to: ## Interoperability -`post-css-variables` plays really nice with [`postcss-nested`](https://github.com/postcss/postcss-nested) in order to get a larger subset of CSS variables features. *See [Nested rules, Usage section](#nested-rules)* +`postcss-css-variables` plays really nice with [`postcss-nested`](https://github.com/postcss/postcss-nested) in order to get a larger subset of CSS variables features. *See [Nested rules, Usage section](#nested-rules)* If you are using [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties) previously, we have a compatible feature set and more so you can switch over without having to refactor any of your code. You can just start writing the new awesome stuff. @@ -230,7 +230,7 @@ There is another similar plugin available, [`postcss-custom-properties`](https:/ ### Differences from `postcss-custom-properties` -The main features that we`post-css-variables` add/provide are: +The main features that we`postcss-css-variables` add/provide are: - No limitation on what scope CSS variables can be declared or used (`:root` or wherever) - Proper value substitution based on explicit DOM/structure traversal diff --git a/index.js b/index.js index 3e4917a..a280c32 100644 --- a/index.js +++ b/index.js @@ -57,8 +57,8 @@ var defaults = { }; module.exports = postcss.plugin('postcss-css-variables', function(options) { - - opts = extend({}, defaults, options); + + var opts = extend({}, defaults, options); // Work with opts here @@ -78,7 +78,7 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) { // Add the js defined variables `opts.variables` to the map map = extend( - map, + map, Object.keys(opts.variables).reduce(function(prevVariableMap, variableName) { var variableEntry = opts.variables[variableName]; // Automatically prefix any variable with `--` (CSS custom property syntax) if it doesn't have it already @@ -187,7 +187,7 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) { var rulesThatHaveDeclarationsWithVariablesList = []; css.eachRule(function(rule) { var doesRuleUseVariables = rule.nodes.some(function(node) { - if(node.type == 'decl') { + if(node.type === 'decl') { var decl = node; // If it uses variables // and is not a variable declarations that we may be preserving from earlier @@ -223,16 +223,16 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) { // Resolve the declarations rulesToWorkOn.forEach(function(ruleToWorkOn) { ruleToWorkOn.nodes.slice(0).forEach(function(node) { - if(node.type == 'decl') { + if(node.type === 'decl') { var decl = node; - resolveDecl(decl, map); + resolveDecl(decl, map, opts.preserve); } }); }); }); - + diff --git a/lib/clone-splice-parent-onto-node-when.js b/lib/clone-splice-parent-onto-node-when.js index 08b94ce..f4bccad 100644 --- a/lib/clone-splice-parent-onto-node-when.js +++ b/lib/clone-splice-parent-onto-node-when.js @@ -35,9 +35,9 @@ var cloneSpliceParentOntoNodeWhen = function(node, parent, /*optional*/whenCb) { // Assign parents to our parent clones cloneParentList.forEach(function(parentClone, index, cloneParentList) { // Keep assigning parents detached until just very end - if(index+1 < cloneParentList.length) { + if(index + 1 < cloneParentList.length) { //parentClone.moveTo(cloneParentList[index+1]); - parentClone.parent = cloneParentList[index+1]; + parentClone.parent = cloneParentList[index + 1]; } }); @@ -45,9 +45,9 @@ var cloneSpliceParentOntoNodeWhen = function(node, parent, /*optional*/whenCb) { // Assign parents to our node clones cloneList.forEach(function(clone, index, cloneList) { // Keep assigning parents detached until just very end - if(index+1 < cloneList.length) { + if(index + 1 < cloneList.length) { //clone.moveTo(cloneList[index+1]); - clone.parent = cloneList[index+1]; + clone.parent = cloneList[index + 1]; // Then splice on the new parent scope } else { // Set the highest parent ancestor to back to where we should splice in @@ -61,4 +61,4 @@ var cloneSpliceParentOntoNodeWhen = function(node, parent, /*optional*/whenCb) { return cloneList[0]; }; -module.exports = cloneSpliceParentOntoNodeWhen; \ No newline at end of file +module.exports = cloneSpliceParentOntoNodeWhen; diff --git a/lib/find-node-ancestor-with-selector.js b/lib/find-node-ancestor-with-selector.js index dd61820..0960e66 100644 --- a/lib/find-node-ancestor-with-selector.js +++ b/lib/find-node-ancestor-with-selector.js @@ -5,10 +5,9 @@ var generateScopeList = require('./generate-scope-list'); var findNodeAncestorWithSelector = function(selector, node) { var matchingNode; - var currentNode = node; - var stillFindingNode = true; // Keep going until we run out of parents to search // or we found the node + var currentNode = node; while(currentNode.parent && !matchingNode) { // A trick to get the selector split up. Generate a scope list on a clone(clean parent) var currentNodeScopeList = generateScopeList(currentNode.clone(), true); @@ -30,4 +29,4 @@ var findNodeAncestorWithSelector = function(selector, node) { return matchingNode; }; -module.exports = findNodeAncestorWithSelector; \ No newline at end of file +module.exports = findNodeAncestorWithSelector; diff --git a/lib/gather-variable-dependencies.js b/lib/gather-variable-dependencies.js index fa206b2..b35ab9d 100644 --- a/lib/gather-variable-dependencies.js +++ b/lib/gather-variable-dependencies.js @@ -47,4 +47,4 @@ var gatherVariableDependencies = function(variablesUsed, map, _dependencyVariabl }; }; -module.exports = gatherVariableDependencies; \ No newline at end of file +module.exports = gatherVariableDependencies; diff --git a/lib/generate-descendant-pieces-from-selector.js b/lib/generate-descendant-pieces-from-selector.js index 6977f8b..4680efb 100644 --- a/lib/generate-descendant-pieces-from-selector.js +++ b/lib/generate-descendant-pieces-from-selector.js @@ -20,4 +20,4 @@ var generateDescendantPiecesFromSelector = function(selector) { }); }; -module.exports = generateDescendantPiecesFromSelector; \ No newline at end of file +module.exports = generateDescendantPiecesFromSelector; diff --git a/lib/generate-direct-descendant-pieces-from-selector.js b/lib/generate-direct-descendant-pieces-from-selector.js index 9b27d62..6306528 100644 --- a/lib/generate-direct-descendant-pieces-from-selector.js +++ b/lib/generate-direct-descendant-pieces-from-selector.js @@ -18,4 +18,4 @@ var generateDirectDescendantPiecesFromSelector = function(selector) { }); }; -module.exports = generateDirectDescendantPiecesFromSelector; \ No newline at end of file +module.exports = generateDirectDescendantPiecesFromSelector; diff --git a/lib/generate-scope-list.js b/lib/generate-scope-list.js index 119ea60..479488e 100644 --- a/lib/generate-scope-list.js +++ b/lib/generate-scope-list.js @@ -50,7 +50,7 @@ var generateScopeList = function(node, /*optional*/includeSelf) { // Add to the front of the array scopeStringPieces.unshift.apply(scopeStringPieces, descendantPieces); - + return scopeStringPieces; }); }); @@ -66,6 +66,6 @@ var generateScopeList = function(node, /*optional*/includeSelf) { } return selectorScopeList; -} +}; -module.exports = generateScopeList; \ No newline at end of file +module.exports = generateScopeList; diff --git a/lib/is-node-under-scope.js b/lib/is-node-under-scope.js index a7743f4..733fbda 100644 --- a/lib/is-node-under-scope.js +++ b/lib/is-node-under-scope.js @@ -8,4 +8,4 @@ var isNodeUnderScope = function(node, scopeNode, /*optional*/ignorePseudo) { return isUnderScope(nodeScopeList, scopeNodeScopeList, ignorePseudo); }; -module.exports = isNodeUnderScope; \ No newline at end of file +module.exports = isNodeUnderScope; diff --git a/lib/is-piece-always-ancestor-selector.js b/lib/is-piece-always-ancestor-selector.js index 1a385eb..d58cdde 100644 --- a/lib/is-piece-always-ancestor-selector.js +++ b/lib/is-piece-always-ancestor-selector.js @@ -9,4 +9,4 @@ var isPieceIsAlwaysAncestorSelector = function(piece) { return !!alwaysAncestorSelector[piece]; }; -module.exports = isPieceIsAlwaysAncestorSelector; \ No newline at end of file +module.exports = isPieceIsAlwaysAncestorSelector; diff --git a/lib/is-under-scope.js b/lib/is-under-scope.js index 3507c2f..9336ef3 100644 --- a/lib/is-under-scope.js +++ b/lib/is-under-scope.js @@ -51,7 +51,7 @@ function getScopeMatchResults(nodeScopeList, scopeNodeScopeList) { // // Or the node scope piece could be an always-ancestor selector itself // And we only want the first occurence so we can keep matching future scope pieces - if(isPieceAlwaysAncestorSelector(scopePiece) || isPieceAlwaysAncestorSelector(nodeScopePiece)) { + if(isPieceAlwaysAncestorSelector(scopePiece) || isPieceAlwaysAncestorSelector(nodeScopePiece)) { foundIndex = overallIndex; break; @@ -76,11 +76,11 @@ function getScopeMatchResults(nodeScopeList, scopeNodeScopeList) { // If it matches completely // or there are still more pieces to match in the future - if(result.doesMatchScope || scopePieceIndex+1 < scopeNodeScopePieces.length) { + if(result.doesMatchScope || scopePieceIndex + 1 < scopeNodeScopePieces.length) { foundIndex = overallIndex; // Move the scope forward the amount that piece consumed // -1 because the of for-loop increments at each iteration - scopePieceIndex += result.scopePieceIndex-1; + scopePieceIndex += result.scopePieceIndex - 1; } break; @@ -90,7 +90,7 @@ function getScopeMatchResults(nodeScopeList, scopeNodeScopeList) { var isFurther = foundIndex >= pieceOffset; - currentPieceOffset = foundIndex+1; + currentPieceOffset = foundIndex + 1; // Mimicing a `[].every` with a for-loop wasEveryPieceFound = wasEveryPieceFound && isFurther; @@ -105,7 +105,7 @@ function getScopeMatchResults(nodeScopeList, scopeNodeScopeList) { return { doesMatchScope: doesMatchScope, - nodeScopePieceIndex: currentPieceOffset-1, + nodeScopePieceIndex: currentPieceOffset - 1, scopePieceIndex: scopePieceIndex }; } @@ -148,4 +148,4 @@ var isUnderScope = function(nodeScopeList, scopeNodeScopeList, /*optional*/ignor isUnderScope.RE_PSEUDO_SELECTOR = RE_PSEUDO_SELECTOR; -module.exports = isUnderScope; \ No newline at end of file +module.exports = isUnderScope; diff --git a/lib/resolve-decl.js b/lib/resolve-decl.js index 166c8e0..bfd37e3 100644 --- a/lib/resolve-decl.js +++ b/lib/resolve-decl.js @@ -8,9 +8,72 @@ var isNodeUnderScope = require('./is-node-under-scope'); var findNodeAncestorWithSelector = require('./find-node-ancestor-with-selector'); var cloneSpliceParentOntoNodeWhen = require('./clone-splice-parent-onto-node-when'); + + +function eachMapItemDependencyOfDecl(variablesUsedList, map, decl, cb) { + // Now find any at-rule declarations that pertains to each rule + // Loop through the variables used + variablesUsedList.forEach(function(variableUsedName) { + + // Find anything in the map that corresponds to that variable + gatherVariableDependencies(variablesUsedList, map).deps.forEach(function(mapItem) { + + var mimicDecl; + if(mapItem.isUnderAtRule) { + + // Get the inner-most selector of the at-rule scope variable declaration we are matching + // Because the inner-most selector will be the same for each branch, we can look at the first one [0] or any of the others + var varDeclScopeList = generateScopeList(mapItem.parent, true); + var innerMostAtRuleSelector = varDeclScopeList[0].slice(-1)[0]; + var nodeToSpliceParentOnto = findNodeAncestorWithSelector(innerMostAtRuleSelector, decl.parent); + + // Splice on where the selector starts matching the selector inside at-rule + // See: `test/fixtures/cascade-on-nested-rules.css` + var varDeclAtRule = mapItem.parent.parent; + mimicDecl = cloneSpliceParentOntoNodeWhen(decl, varDeclAtRule, function(ancestor) { + return ancestor === nodeToSpliceParentOnto; + }); + + + //console.log('amd og', generateScopeList(decl.parent, true)); + //console.log('amd', generateScopeList(mimicDecl.parent, true)); + //console.log(generateScopeList(mapItem.parent, true)); + //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; + + // 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); + } + }); + }); +} + + + + // Resolve the decl with the computed value // Also add in any media queries that change the value as necessary -function resolveDecl(decl, map, /*optional*/logResolveValueResult) { +function resolveDecl(decl, map, /*optional*/shouldPreserve, /*optional*/logResolveValueResult) { + shouldPreserve = shouldPreserve || false; + + // Make it chainable var _logResolveValueResult = function(valueResults) { if(logResolveValueResult) { logResolveValueResult(valueResults); @@ -23,7 +86,6 @@ function resolveDecl(decl, map, /*optional*/logResolveValueResult) { // Grab the balue for this declarations var valueResults = _logResolveValueResult(resolveValue(decl, map)); - // Resolve the cascade dependencies @@ -74,72 +136,17 @@ function resolveDecl(decl, map, /*optional*/logResolveValueResult) { // If we are preserving var(...) usage and the value changed meaning it had some - if(opts.preserve === true && decl.value !== valueResults.value) { + if(shouldPreserve === true && decl.value !== valueResults.value) { decl.cloneAfter(); } // Set the new value after we are done dealing with at-rule stuff decl.value = valueResults.value; - } -function eachMapItemDependencyOfDecl(variablesUsedList, map, decl, cb) { - // Now find any at-rule declarations that pertains to each rule - // Loop through the variables used - variablesUsedList.forEach(function(variableUsedName) { - - // Find anything in the map that corresponds to that variable - gatherVariableDependencies(variablesUsedList, map).deps.forEach(function(mapItem) { - - var mimicDecl; - if(mapItem.isUnderAtRule) { - - // Get the inner-most selector of the at-rule scope variable declaration we are matching - // Because the inner-most selector will be the same for each branch, we can look at the first one [0] or any of the others - var varDeclScopeList = generateScopeList(mapItem.parent, true); - var innerMostAtRuleSelector = varDeclScopeList[0].slice(-1)[0]; - var nodeToSpliceParentOnto = findNodeAncestorWithSelector(innerMostAtRuleSelector, decl.parent); - - // Splice on where the selector starts matching the selector inside at-rule - // See: `test/fixtures/cascade-on-nested-rules.css` - var varDeclAtRule = mapItem.parent.parent; - mimicDecl = cloneSpliceParentOntoNodeWhen(decl, varDeclAtRule, function(ancestor) { - return ancestor === nodeToSpliceParentOnto; - }); - - - //console.log('amd og', generateScopeList(decl.parent, true)); - //console.log('amd', generateScopeList(mimicDecl.parent, true)); - //console.log(generateScopeList(mapItem.parent, true)); - //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; - - // 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); - } - }); - }); -} -module.exports = resolveDecl; \ No newline at end of file +module.exports = resolveDecl; diff --git a/lib/resolve-value.js b/lib/resolve-value.js index e1b2115..3752c5b 100644 --- a/lib/resolve-value.js +++ b/lib/resolve-value.js @@ -16,9 +16,9 @@ var RE_VAR_FUNC = (/var\((--[^,\s]+?)(?:\s*,\s*(.+))?\)/); // and we can figure out the final value // // `ignorePseudoScope`: Optional bool to determine whether the scope resolution should be left alone or not -// +// // Note: We do not modify the declaration -// Note: Resolving a declaration value without any `var(...)` does not harm the final value. +// Note: Resolving a declaration value without any `var(...)` does not harm the final value. // This means, feel free to run everything through this function var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal debugging*/_debugIsInternal) { @@ -75,12 +75,12 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal return ancestor === nodeToSpliceParentOnto; }); - replaceValue = resolveValue(matchingMimicDecl, map, /*internal*/true).value; + replaceValue = resolveValue(matchingMimicDecl, map, false, /*internal*/true).value; } isResultantValueUndefined = replaceValue === undefined; if(isResultantValueUndefined) { - warnings.push(["variable '" + variableName + "' is undefined and used without a fallback", { node: decl }]); + warnings.push(['variable ' + variableName + ' is undefined and used without a fallback', { node: decl }]); } return replaceValue; @@ -99,4 +99,4 @@ var resolveValue = function(decl, map, /*optional*/ignorePseudoScope, /*internal resolveValue.RE_VAR_FUNC = RE_VAR_FUNC; -module.exports = resolveValue; \ No newline at end of file +module.exports = resolveValue; diff --git a/package.json b/package.json index ba59dad..37cb9a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postcss-css-variables", - "version": "0.3.8", + "version": "0.3.9", "description": "PostCSS plugin to transform CSS Custom Properties(CSS variables) syntax into a static representation", "keywords": [ "postcss",