postcss-extend
Advanced tools
Comparing version 0.4.2 to 0.4.3
# Changelog | ||
## v0.4.1 | ||
- [Infinite recursion detection improvements, widespread minor corrections](https://github.com/travco/postcss-extend/releases/tag/v0.4.1) | ||
## v0.4.3 | ||
- [Antipattern CSS detection and warning, and media-cross-media warning](https://github.com/travco/postcss-extend/releases/tag/v0.4.3) | ||
## v0.4.2 | ||
- [Infinite recursion detection improvements, widespread minor corrections](https://github.com/travco/postcss-extend/releases/tag/v0.4.2) | ||
## v0.4.0 | ||
@@ -7,0 +10,0 @@ - [All-intended-features release as 'postcss-extend'](https://github.com/travco/postcss-extend/releases/tag/v0.4.0) |
67
index.js
@@ -11,2 +11,3 @@ 'use strict'; | ||
var recurseStack = []; | ||
var isAntiPatternCSS = false; | ||
@@ -21,2 +22,4 @@ css.eachAtRule(function(atRule) { | ||
//Selectively disclude silents and placeholders, find unused, | ||
//and exclude from the final output | ||
css.eachRule(function(targetNode) { | ||
@@ -41,2 +44,3 @@ var tgtSaved = targetNode.selectors; | ||
//simplification process to find definitions in the future | ||
function processDefinition(atRule) { | ||
@@ -49,2 +53,5 @@ 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; | ||
@@ -84,2 +91,3 @@ atRule.nodes.forEach(function(node) { | ||
var tgtSaved = targetNode.selectors; | ||
//Strip all @define-placeholders and save slug-selectors present in tgtSaved | ||
for (var i = 0; i < tgtSaved.length; i++) { | ||
@@ -93,4 +101,7 @@ if (tgtSaved[i].substring(0, 20) === '@define-placeholder ') { | ||
for (var n = 0; n < tgtSaved.length; n++) { | ||
//Operate on normal extendables | ||
if (atRule.params === tgtSaved[n]) { | ||
//check if target has unresolved extensions, then extend them | ||
if (extensionRecursionHandler(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. | ||
processExtension(atRule); | ||
@@ -103,2 +114,3 @@ couldExtend = true; | ||
couldExtend = true; | ||
//Operate on sub-elements of extendables (thus extending them) | ||
} else if (tgtSaved[n].substring(1).search(/[\s.:#]/) + 1 !== -1) { | ||
@@ -108,3 +120,5 @@ var tgtBase = tgtSaved[n].substring(0, tgtSaved[n].substring(1).search(/[\s.:#]/) + 1); | ||
if (atRule.params === tgtBase) { | ||
//check if target rule has unresolved extensions, then extend them | ||
if (extensionRecursionHandler(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. | ||
processExtension(atRule); | ||
@@ -114,13 +128,16 @@ couldExtend = true; | ||
} | ||
//tack onto target node | ||
tgtAccumulate = tgtAccumulate.concat(formSubSelector(originSels, tgtSub)); | ||
couldExtend = true; | ||
} | ||
} | ||
}//END OF sub root-extensions | ||
} | ||
if (couldExtend) { | ||
//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 { | ||
@@ -130,10 +147,26 @@ var backFirstTargetNode; | ||
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); | ||
//If is in @media extending another @media and is a targeted rule (two phase): | ||
//check for an illegal extention, and then don't process that node. | ||
} else if (subRule.selectors.indexOf(atRule.params) !== -1) { | ||
isBadExtensionPair(atRule, subRule); | ||
} else { | ||
for (var s = 0; s < subRule.selectors.length; s++) { | ||
if (subRule.selectors[s].substring(1).search(/[\s.:#]/) + 1 !== -1 && subRule.selectors[s].substring(0, subRule.selectors[s].substring(1).search(/[\s.:#]/) + 1) === atRule.params) { | ||
isBadExtensionPair(atRule, subRule); | ||
break; | ||
} | ||
} | ||
} | ||
}); | ||
}); //end of each rule | ||
while (targetNodeArray.length > 0) { | ||
backFirstTargetNode = targetNodeArray.pop(); | ||
if (backFirstTargetNode.selectors.indexOf(atRule.params) !== -1) { | ||
//check if rule has unresolved extensions, then extend them | ||
if (extensionRecursionHandler(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. | ||
processExtension(atRule); | ||
@@ -143,5 +176,7 @@ couldExtend = true; | ||
} | ||
//In scope, tack on selector to target rule | ||
if (backFirstTargetNode.parent === atRule.parent.parent) { | ||
selectorRetainer = backFirstTargetNode.selectors; | ||
backFirstTargetNode.selector = uniqreq(selectorRetainer.concat(originSels)).join(', '); | ||
//Out of scope, direcly copy declarations | ||
} else { | ||
@@ -152,2 +187,3 @@ safeCopyDeclarations(backFirstTargetNode, atRule.parent); | ||
} else { | ||
//Pull from sub-elements of target nodes (thus extending them) | ||
for (var m = 0; m < backFirstTargetNode.selectors.length; m++) { | ||
@@ -157,3 +193,5 @@ 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 extensions, then extend them | ||
if (extensionRecursionHandler(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. | ||
processExtension(atRule); | ||
@@ -164,9 +202,13 @@ couldExtend = true; | ||
if (backFirstTargetNode.parent === atRule.parent.parent) { | ||
//Use Tacking onto exiting selectors instead of new creation | ||
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 extension | ||
safeCopyDeclarations(backFirstTargetNode, subTarget.node); | ||
} else { | ||
//create additional nodes below existing for each instance of subs | ||
var newNode = postcss.rule(); | ||
@@ -184,5 +226,5 @@ newNode.semicolon = atRule.semicolon; | ||
} | ||
} | ||
} //end of if hasMediaAncestor | ||
if (!couldExtend) { | ||
result.warn('\'' + atRule.params + '\', has not been defined, so cannot be extended'); | ||
result.warn('\'' + atRule.params + '\', has not been defined, so it cannot be extended'); | ||
} | ||
@@ -235,2 +277,9 @@ if (atRule.parent !== undefined) { | ||
function isBadExtensionPair(atRule, targetNode) { | ||
if (hasMediaAncestor(targetNode) && hasMediaAncestor(atRule) && targetNode.parent !== atRule.parent.parent) { | ||
result.warn('@extend was called to extend something in an @media from within another @media, this was safely ignored. For more information see the README under \'Quirks\'', {node: targetNode}); | ||
return true; | ||
} | ||
} | ||
function hasMediaAncestor(node) { | ||
@@ -263,2 +312,3 @@ var parent = node.parent; | ||
var clone = node.clone(); | ||
//For lack of a better way to analyse how much tabbing is required: | ||
if (nodeOrigin.parent === nodeDest.parent) { | ||
@@ -301,2 +351,7 @@ clone.before = node.before; | ||
if (recurseStack.length === 0) { isTopOfRecurse = true; } | ||
if (!isAntiPatternCSS && css.index(atRule.parent) < css.index(targetNode)) { | ||
//throw this error only once, and only if it's an antipattern | ||
result.warn('@extend is being used in an anti-pattern (extending things not yet defined). This is your first and final warning', {node: atRule.parent}); | ||
isAntiPatternCSS = true; | ||
} | ||
if (recursableRule.bool) { | ||
@@ -310,2 +365,3 @@ recurseStack.push(atRule.params); | ||
result.warn('Infinite extension recursion detected', { node: atRule }); | ||
//clean out the recurse stack of duplicates (from early aborts like this) before dropping | ||
recurseStack = uniqreq(recurseStack); | ||
@@ -319,2 +375,3 @@ return false; | ||
} | ||
//Empty history if this is top of a recursion (as process preserves detections as it backs-out) | ||
if (isTopOfRecurse) { | ||
@@ -321,0 +378,0 @@ recurseStack = []; |
@@ -8,4 +8,4 @@ { | ||
"license": "MIT", | ||
"version": "0.4.2", | ||
"description": "Simple extends for PostCSS", | ||
"version": "0.4.3", | ||
"description": "As close to cssnext @extend as possible for PostCSS", | ||
"homepage": "https://github.com/travco/postcss-extend", | ||
@@ -12,0 +12,0 @@ "repository": { |
@@ -287,3 +287,3 @@ # postcss-extend [![Build Status](https://travis-ci.org/travco/postcss-extend.svg?branch=master)](https://travis-ci.org/travco/postcss-extend) | ||
**Don't**. It's currently directly-disallowed in code to prevent unexpected things from happening. The current expectation is that the only time majority of users would do this is when making a mistake. That expectation remains unless someone can present a solution and a logical way of handling this (not in the native CSS parser) that is also a realistic common-use case. | ||
**Don't**. It's currently directly-disallowed in code to prevent unexpected things from happening, and will throw an error to warn you. The current expectation is that the only time majority of users would do this is when making a mistake. That expectation remains unless someone can present a solution and a logical way of handling this (not in the native CSS parser) that is also a realistic common-use case. | ||
@@ -356,3 +356,3 @@ #### Chaining `@extend`s, or extension-recursion | ||
**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. | ||
**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 (anti-pattern CSS). So if you want to keep processing time down, write good CSS. If you're curious if what you're writing is an anti-pattern, don't worry, it will throw a warning. | ||
@@ -359,0 +359,0 @@ **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. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35715
365