postcss-extend
Advanced tools
Comparing version 0.4.0 to 0.4.1
# Changelog | ||
## v0.4.1 | ||
- [Infinite recursion detection improvements, widespread minor corrections](https://github.com/travco/postcss-extend/releases/tag/v0.4.1) | ||
## v0.4.0 | ||
- [All-intended-features release as 'postcss-extend'](https://github.com/travco/postcss-extend/releases/tag/v0.4.0) | ||
## v0.3.1 | ||
@@ -4,0 +10,0 @@ - Ensure that unused defining at-rules and erroneous extending at-rules are removed from generated CSS. |
112
index.js
'use strict'; | ||
var postcss = require('postcss'); | ||
// /*DEBUG*/ var appendout = require('fs').appendFileSync; | ||
@@ -13,4 +12,2 @@ module.exports = postcss.plugin('postcss-simple-extend', function simpleExtend() { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n----------------------------------------'); | ||
css.eachAtRule(function(atRule) { | ||
@@ -24,4 +21,2 @@ if (definingAtRules.indexOf(atRule.name) !== -1) { | ||
// Selectively disclude silents and placeholders, find unused, | ||
// and exclude from the final output | ||
css.eachRule(function(targetNode) { | ||
@@ -39,4 +34,2 @@ var tgtSaved = targetNode.selectors; | ||
targetNode.removeSelf(); | ||
// /*DEBUG*/ } else { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nSifted out placeholder/silent ' + tgtSaved[i]); | ||
} | ||
@@ -48,3 +41,2 @@ } | ||
}); | ||
//simplification process to find definitions in the future | ||
function processDefinition(atRule) { | ||
@@ -57,5 +49,2 @@ if (isBadDefinitionLocation(atRule)) { | ||
// Manually copy styling properties (semicolon, whitespace) | ||
// to newly created and cloned nodes, | ||
// cf. https://github.com/postcss/postcss/issues/85 | ||
definition.semicolon = atRule.semicolon; | ||
@@ -71,3 +60,2 @@ atRule.nodes.forEach(function(node) { | ||
definition.selector = '@define-placeholder ' + atRule.params.toString(); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nDeclaring placeholder : ' + definition.selector); | ||
atRule.parent.insertBefore(atRule, definition); | ||
@@ -87,3 +75,3 @@ atRule.removeSelf(); | ||
var originSels = atRule.parent.selectors; | ||
var selectorRetainer; | ||
var selectorRetainer = []; | ||
var couldExtend = false; | ||
@@ -98,8 +86,5 @@ var subTarget = { | ||
var tgtSaved = targetNode.selectors; | ||
//Strip all @define-placeholders and save slug-selectors present in tgtSaved | ||
for (var i = 0; i < tgtSaved.length; i++) { | ||
if (tgtSaved[i].substring(0, 20) === '@define-placeholder ') { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nn[' + i + ']String = ' + tgtSaved[i] + ' Substring 0-20 = \'' + tgtSaved[i].substring(0, 20) + '\''); | ||
tgtSaved[i] = tgtSaved[i].substring(20, (tgtSaved[i].length)); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nresString = \'' + tgtSaved[i] + '\''); | ||
} | ||
@@ -110,8 +95,4 @@ } | ||
for (var n = 0; n < tgtSaved.length; n++) { | ||
// Operate on normal extendables | ||
if (atRule.params === tgtSaved[n]) { | ||
//check if target has unresolved extentions, then extend them | ||
if (extentionRecursionHandler(atRule, targetNode)) { | ||
// We need to re-evaluate the current atRule, as other classes (once passed over) may now be matching, so re-process and exit. | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n!Bumping evaluation of :' + atRule.parent); | ||
if (extensionRecursionHandler(atRule, targetNode)) { | ||
processExtension(atRule); | ||
@@ -121,8 +102,5 @@ couldExtend = true; | ||
} | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nfound and extending : ' + tgtSaved[n] + ' : ' + originSels); | ||
tgtAccumulate = tgtAccumulate.concat(originSels); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nCombined selectors :\n' + tgtAccumulate); | ||
couldExtend = true; | ||
//Operate on sub-elements of extendables (thus extending them) | ||
} else if (tgtSaved[n].substring(1).search(/[\s.:#]/) + 1 !== -1) { | ||
@@ -132,6 +110,3 @@ var tgtBase = tgtSaved[n].substring(0, tgtSaved[n].substring(1).search(/[\s.:#]/) + 1); | ||
if (atRule.params === tgtBase) { | ||
//check if target rule has unresolved extentions, then extend them | ||
if (extentionRecursionHandler(atRule, targetNode)) { | ||
// We need to re-evaluate the current atRule, as other classes (once passed over) may now be matching, so re-process and exit. | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n!Bumping evaluation of :' + atRule.parent); | ||
if (extensionRecursionHandler(atRule, targetNode)) { | ||
processExtension(atRule); | ||
@@ -141,41 +116,25 @@ couldExtend = true; | ||
} | ||
//tack onto target node | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nfound and extending : ' + tgtSaved[n].substring(0, tgtSaved[n].substring(1).search(/[\s.:#]/) + 1) + ' :\n' + tgtBase + ' (' + tgtSub + ')'); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nCalling formSubSelector with (\n' + originSels + ',\n' + tgtSub); | ||
tgtAccumulate = tgtAccumulate.concat(formSubSelector(originSels, tgtSub)); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nCombined selectors :\n' + tgtAccumulate); | ||
couldExtend = true; | ||
} | ||
}//END OF sub root-extentions | ||
} | ||
} | ||
if (couldExtend) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nStart uniqreq2 :\n' + tgtAccumulate); | ||
//Kill off duplicate selectors | ||
tgtAccumulate = uniqreq(tgtAccumulate).toString().replace(/,/g, ', '); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nPost uniqreq2 :\n' + tgtAccumulate); | ||
targetNode.selector = tgtAccumulate; | ||
} | ||
}); | ||
//hasMediaAncestor === true: --------------- | ||
} else { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nAttempting to fetch declarations for ' + atRule.params + '...'); | ||
var backFirstTargetNode; | ||
var targetNodeArray = []; | ||
css.eachRule(function(subRule) { | ||
//create a back-is-top stack so that we can efficiently operate on nodes in reverse | ||
//thus retaining priority when copying declarations if there are multiple matches | ||
if (!hasMediaAncestor(subRule) || subRule.parent === atRule.parent.parent) { | ||
targetNodeArray.push(subRule); | ||
} // /*DEBUG*/ else { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n\'' + atRule.params + '\' ignored possible target in another @media : \n' + subRule); | ||
// /*DEBUG*/ } | ||
}); //end of each rule | ||
} | ||
}); | ||
while (targetNodeArray.length > 0) { | ||
backFirstTargetNode = targetNodeArray.pop(); | ||
if (backFirstTargetNode.selectors.indexOf(atRule.params) !== -1) { | ||
//check if rule has unresolved extentions, then extend them | ||
if (extentionRecursionHandler(atRule, backFirstTargetNode)) { | ||
// We need to re-evaluate the current atRule, as other classes (once passed over) may now be matching, so re-process and exit. | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n!Bumping evaluation of :' + atRule.parent); | ||
if (extensionRecursionHandler(atRule, backFirstTargetNode)) { | ||
processExtension(atRule); | ||
@@ -185,10 +144,6 @@ couldExtend = true; | ||
} | ||
//In scope, tack on selector to target rule | ||
if (backFirstTargetNode.parent === atRule.parent.parent) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n...tacking onto backFirstTargetNode :' + backFirstTargetNode); | ||
selectorRetainer = backFirstTargetNode.selector; | ||
backFirstTargetNode.selector = selectorRetainer + ', ' + originSels.join(', '); | ||
//Out of scope, direcly copy declarations | ||
selectorRetainer = backFirstTargetNode.selectors; | ||
backFirstTargetNode.selector = uniqreq(selectorRetainer.concat(originSels)).join(', '); | ||
} else { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n...grabbing backFirstTargetNode :\n' + backFirstTargetNode); | ||
safeCopyDeclarations(backFirstTargetNode, atRule.parent); | ||
@@ -198,3 +153,2 @@ } | ||
} else { | ||
//Pull from sub-elements of target nodes (thus extending them) | ||
for (var m = 0; m < backFirstTargetNode.selectors.length; m++) { | ||
@@ -204,6 +158,3 @@ var extTgtBase = backFirstTargetNode.selectors[m].substring(0, backFirstTargetNode.selectors[m].substring(1).search(/[\s.:#]/) + 1); | ||
if (backFirstTargetNode.selectors[m].substring(1).search(/[\s.:#]/) + 1 !== -1 && extTgtBase === atRule.params) { | ||
//check if target rule has unresolved extentions, then extend them | ||
if (extentionRecursionHandler(atRule, backFirstTargetNode)) { | ||
// We need to re-evaluate the current atRule, as other classes (once passed over) may now be matching, so re-process and exit. | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n!Bumping evaluation of :' + atRule.parent); | ||
if (extensionRecursionHandler(atRule, backFirstTargetNode)) { | ||
processExtension(atRule); | ||
@@ -214,16 +165,9 @@ couldExtend = true; | ||
if (backFirstTargetNode.parent === atRule.parent.parent) { | ||
//Use Tacking onto exiting selectors instead of new creation | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nUtilizing existing brother subclass for extention, as nothing matches: \n' + atRule.parent.selector + ' sub-' + extTgtSub); | ||
selectorRetainer = backFirstTargetNode.selector; | ||
backFirstTargetNode.selector = selectorRetainer + ', ' + formSubSelector(originSels, extTgtSub).join(', '); | ||
selectorRetainer = backFirstTargetNode.selectors; | ||
backFirstTargetNode.selector = uniqreq(selectorRetainer.concat(formSubSelector(originSels, extTgtSub))).join(', '); | ||
} else { | ||
//check for prexisting sub classes before making one | ||
subTarget = findBrotherSubClass(atRule.parent, extTgtSub); | ||
if (subTarget.bool) { | ||
//utilize existing subclass for extention | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nUtilizing existing subclass for extention:\n' + subTarget); | ||
safeCopyDeclarations(backFirstTargetNode, subTarget.node); | ||
} else { | ||
//create additional nodes below existing for each instance of subs | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nUtilizing new subclass for extention, as nothing matches: \n' + atRule.parent.selector + ' sub-' + extTgtSub); | ||
var newNode = postcss.rule(); | ||
@@ -241,6 +185,5 @@ newNode.semicolon = atRule.semicolon; | ||
} | ||
} //end of if hasMediaAncestor | ||
} | ||
if (!couldExtend) { | ||
result.warn('\'' + atRule.params + '\', has not been defined, so cannot be extended'); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n\'' + atRule.params + '\' has not been defined!!!'); | ||
} | ||
@@ -317,8 +260,5 @@ if (atRule.parent !== undefined) { | ||
if (nodeDest.some(function(decl) { return decl.prop === node.prop; })) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nsafeIgnored : ' + node + ' for ' + nodeDest.selector); | ||
return; | ||
} | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nnodeDest Nodes:\n' + nodeDest.nodes); | ||
var clone = node.clone(); | ||
//For lack of a better way to analyse how much tabbing is required: | ||
if (nodeOrigin.parent === nodeDest.parent) { | ||
@@ -357,5 +297,6 @@ clone.before = node.before; | ||
function extentionRecursionHandler(atRule, targetNode) { | ||
function extensionRecursionHandler(atRule, targetNode) { | ||
var recursableRule = findUnresolvedExtendChild(targetNode); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nopen eRH recurseStack : ' + recurseStack); | ||
var isTopOfRecurse = false; | ||
if (recurseStack.length === 0) { isTopOfRecurse = true; } | ||
if (recursableRule.bool) { | ||
@@ -365,13 +306,7 @@ recurseStack.push(atRule.params); | ||
if (recurseStack.indexOf(recursableRule.node.params) === -1) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nRecursing from ' + atRule.parent.selector + ' on: ' + recursableRule.node.parent + '\n\\/\\/\\/\\/\\/\\/\\/\\/ ' + recurseStack.length); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\npre-process recurseStack : ' + recurseStack); | ||
processExtension(recursableRule.node); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n ^ ^ ^ ^ ^ ^ ^ ^ ' + recurseStack.length); | ||
recursableRule = findUnresolvedExtendChild(targetNode); | ||
} else { | ||
result.warn('Infinite extention recursion detected', { node: atRule }); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nInfinite Recursion detected, recurseStack : ' + recurseStack + '\n -- on :\n' + atRule.parent + '\n!!!!!!!!!!!!'); | ||
//clean out the recurse stack of duplicates (from early aborts) before popping | ||
result.warn('Infinite extension recursion detected', { node: atRule }); | ||
recurseStack = uniqreq(recurseStack); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\npost-uniqreq recurseStack : ' + recurseStack); | ||
return false; | ||
@@ -381,8 +316,8 @@ } | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\npre-pop recurseStack : ' + recurseStack); | ||
// if (recurseStack.pop() !== atRule.params) { | ||
// result.warn('Detected mis-aligned recursion stack! (Please post your CSS in a github issue, this shouldn\'t happen!)', { node: atRule }); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n!!!!!!!!!!!!MISALIGNED RECURSE STACK\npost-pop recurseStack : ' + recurseStack + ''); | ||
// } | ||
// Signal to do a recall and exit (only happens with badly formed css) | ||
if (recurseStack.pop() !== atRule.params && recurseStack.indexOf(atRule.params) === -1) { | ||
result.warn('Detected critically mis-aligned recursion stack! (Please post your CSS in a github issue, this shouldn\'t ever happen!)', { node: atRule }); | ||
} | ||
if (isTopOfRecurse) { | ||
recurseStack = []; | ||
} | ||
return true; | ||
@@ -404,3 +339,2 @@ } | ||
seldiff = uniqreq(seldiff); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nseldiff : ' + seldiff + '\n\tBetween:\n' + node.selectors + '\n\tand:\n' + selectorAccumulator); | ||
if (seldiff.length === selectorAccumulator.length) { | ||
@@ -407,0 +341,0 @@ foundNode = node; |
@@ -8,3 +8,3 @@ { | ||
"license": "MIT", | ||
"version": "0.4.0", | ||
"version": "0.4.1", | ||
"description": "Simple extends for PostCSS", | ||
@@ -11,0 +11,0 @@ "homepage": "https://github.com/travco/postcss-extend", |
@@ -9,3 +9,3 @@ # postcss-extend [](https://travis-ci.org/travco/postcss-extend) | ||
- Define an 'silent' extendable selector — a "placeholder selector" — to which you can (from anywhere in the doc), add concrete selectors from other rule sets. | ||
- Add concrete selectors from one rule (containing the `@extend`) to all rule sets with the selector specified (or a sub class of the one specified). | ||
- Add concrete selectors from one rule (containing the `@extend`) to all rule sets with the selector specified (or a subclass of the one specified). | ||
- Pull-in declarations in rulesets (most) anywhere in the doc (by a selector) from within `@media` statements (semi-safely) | ||
@@ -53,4 +53,2 @@ - Extend existing media-conscious rulesets, even if some of them are within `@media` statements. | ||
## Installation | ||
This is just a fork, not an NPM module yet :(, it's also still in development, the origin is here, use it in the mean-time: | ||
``` | ||
@@ -66,3 +64,3 @@ npm install postcss-extend --save | ||
- [Defining Placeholders](https://github.com/travco/postcss-extend#extending-rules-or-placeholders) | ||
- [Defining Placeholders](https://github.com/travco/postcss-extend#defining-placeholders) | ||
- [The '%' placeholder](https://github.com/travco/postcss-extend#the--silent-placeholder) | ||
@@ -76,3 +74,3 @@ - [Extending Rules or Placeholders](https://github.com/travco/postcss-extend#extending-rules-or-placeholders) | ||
- [Extending something in an @media while inside an @media](https://github.com/travco/postcss-extend#extending-something-in-an-media-while-inside-an-media) | ||
- [Chaining `@extend`s, or Extention-Recursion](https://github.com/travco/postcss-extend#chaining-extends-or-extention-recursion) | ||
- [Chaining `@extend`s, or Extension-Recursion](https://github.com/travco/postcss-extend#chaining-extends-or-extension-recursion) | ||
@@ -123,3 +121,3 @@ ### Defining Placeholders | ||
Whenever extending a rule or placeholder, you are also automatically trying to extend any sub classes or elements that have *exactly* what you selected (before a space, `.`, `:`, or `#`). For example: | ||
Whenever extending a rule or placeholder, you are also automatically trying to extend any subclasses or elements that have *exactly* what you selected (before a space, `.`, `:`, or `#`). For example: | ||
```css | ||
@@ -203,3 +201,3 @@ .potato { | ||
So what does it do when sub classes of the extended rule are also outside `@media`? | ||
So what does it do when subclasses of the extended rule are also outside `@media`? | ||
```css | ||
@@ -297,3 +295,3 @@ .potato { | ||
#### Chaining `@extend`s, or extention-recursion | ||
#### Chaining `@extend`s, or extension-recursion | ||
@@ -362,8 +360,8 @@ Definately one of the more powerful features of SASS's `@extend` is here too. It does however, come with a slight caveat that it is order-agnostic. Meaning that it doesn't enforce order by only extending that which came above it, it just goes. | ||
## Quirks | ||
As with any piece of code it's got a few quirks. Behaviors that are not intended, and not enforced, and may disappear (or be forcively altered) with the next release, so it's useful to be aware of them. | ||
As with any piece of code it's got a few quirks. Behaviors that are not intended, and not enforced, and may disappear (or be forcibly altered) with the next release, so it's useful to be aware of them. | ||
**Order of Processing** : Currently, all of the `@extend`s being processed are run in a sequential manner from the top to the bottom of the doc. This keeps thing relatively snappy, but makes it so that we have to do conditional-recursion on not-yet-declared-or-extended rules. This leads to some blatant inefficiencies when processing badly formed CSS. So if you want to keep processing time down, write good CSS. | ||
**Non-logical means of extention for `@media`** : As anyone who's aware of the complications discussed in the [SASS issue about extending across `@media`](https://github.com/sass/sass/issues/1050) would know. There is no way (known) of extending when `@media` rules are involved that is both 'clean and simple' and 'logically correct with how `@extend` is used elsewhere'. The way this plugin operates, and it's logical meaning, is a blatant compromise so that it has both common use cases and easier implementation. While the current implementations will not change (without flags), such things as extending an `@media` from within an `@media` does nothing, this could possibly change in the future. | ||
**Non-logical means of extension for `@media`** : As anyone who's aware of the complications discussed in the [SASS issue about extending across `@media`](https://github.com/sass/sass/issues/1050) would know. There is no way (known) of extending when `@media` rules are involved that is both 'clean and simple' and 'logically correct with how `@extend` is used elsewhere'. The way this plugin operates, and it's logical meaning, is a blatant compromise so that it has both common use cases and easier implementation. While the current implementations will not change (without flags), such things as extending an `@media` from within an `@media` does nothing, this could possibly change in the future. | ||
##### Originally a fork of davidtheclark's [postcss-simple-extend](https://github.com/davidtheclark/postcss-simple-extend) (extended) by way of the included [MIT License](https://github.com/travco/postcss-extend/blob/master/LICENSE) | ||
##### Originally a fork of davidtheclark's [postcss-simple-extend](https://github.com/davidtheclark/postcss-simple-extend) (extended) by way of the included [MIT License](https://github.com/travco/postcss-extend/blob/master/LICENSE) |
31144
309
360