postcss-extend
Advanced tools
Comparing version 0.4.3 to 1.0.0
# Changelog | ||
## v1.0.0 | ||
- [Updated to PostCSS 5](https://github.com/travco/postcss-extend/releases/tag/v1.0.0) | ||
## v0.4.3 | ||
@@ -4,0 +7,0 @@ - [Antipattern CSS detection and warning, and media-cross-media warning](https://github.com/travco/postcss-extend/releases/tag/v0.4.3) |
81
index.js
'use strict'; | ||
var postcss = require('postcss'); | ||
// /*DEBUG*/ var appendout = require('fs').appendFileSync; | ||
@@ -13,3 +14,5 @@ module.exports = postcss.plugin('postcss-simple-extend', function simpleExtend() { | ||
css.eachAtRule(function(atRule) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n----------------------------------------'); | ||
css.walkAtRules(function(atRule) { | ||
if (definingAtRules.indexOf(atRule.name) !== -1) { | ||
@@ -24,3 +27,3 @@ processDefinition(atRule); | ||
//and exclude from the final output | ||
css.eachRule(function(targetNode) { | ||
css.walkRules(function(targetNode) { | ||
var tgtSaved = targetNode.selectors; | ||
@@ -36,3 +39,5 @@ var selectorAccumulator; | ||
} else if (tgtSaved.length === 1) { | ||
targetNode.removeSelf(); | ||
targetNode.remove(); | ||
// /*DEBUG*/ } else { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nSifted out placeholder/silent ' + tgtSaved[i]); | ||
} | ||
@@ -48,3 +53,3 @@ } | ||
if (isBadDefinitionLocation(atRule)) { | ||
atRule.removeSelf(); | ||
atRule.remove(); | ||
return; | ||
@@ -57,14 +62,15 @@ } | ||
//cf. https://github.com/postcss/postcss/issues/85 | ||
definition.semicolon = atRule.semicolon; | ||
definition.raws.semicolon = atRule.raws.semicolon; | ||
atRule.nodes.forEach(function(node) { | ||
if (isBadDefinitionNode(node)) return; | ||
var clone = node.clone(); | ||
clone.before = node.before; | ||
clone.after = node.after; | ||
clone.between = node.between; | ||
clone.raws.before = node.raws.before; | ||
clone.raws.after = node.raws.after; | ||
clone.raws.between = node.raws.between; | ||
definition.append(clone); | ||
}); | ||
definition.selector = '@define-placeholder ' + atRule.params.toString(); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nDeclaring placeholder : ' + definition.selector); | ||
atRule.parent.insertBefore(atRule, definition); | ||
atRule.removeSelf(); | ||
atRule.remove(); | ||
} | ||
@@ -75,5 +81,5 @@ | ||
if (!atRule.parent.nodes.length || (atRule.parent.nodes.length === 1 && atRule.parent.type !== 'root')) { | ||
atRule.parent.removeSelf(); | ||
atRule.parent.remove(); | ||
} else { | ||
atRule.removeSelf(); | ||
atRule.remove(); | ||
} | ||
@@ -91,3 +97,3 @@ return; | ||
if (!hasMediaAncestor(atRule)) { | ||
css.eachRule(function(targetNode) { | ||
css.walkRules(function(targetNode) { | ||
var tgtSaved = targetNode.selectors; | ||
@@ -97,3 +103,5 @@ //Strip all @define-placeholders and save slug-selectors present in tgtSaved | ||
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] + '\''); | ||
} | ||
@@ -109,2 +117,3 @@ } | ||
//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); | ||
processExtension(atRule); | ||
@@ -114,4 +123,6 @@ 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; | ||
@@ -126,2 +137,3 @@ //Operate on sub-elements of extendables (thus extending them) | ||
//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); | ||
processExtension(atRule); | ||
@@ -132,3 +144,7 @@ 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; | ||
@@ -139,2 +155,3 @@ } | ||
if (couldExtend) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nStart uniqreq2 :\n' + tgtAccumulate); | ||
//Kill off duplicate selectors | ||
@@ -148,5 +165,6 @@ tgtAccumulate = uniqreq(tgtAccumulate).toString().replace(/,/g, ', '); | ||
} else { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nAttempting to fetch declarations for ' + atRule.params + '...'); | ||
var backFirstTargetNode; | ||
var targetNodeArray = []; | ||
css.eachRule(function(subRule) { | ||
css.walkRules(function(subRule) { | ||
//create a back-is-top stack so that we can efficiently operate on nodes in reverse | ||
@@ -176,2 +194,3 @@ //thus retaining priority when copying declarations if there are multiple matches | ||
//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); | ||
processExtension(atRule); | ||
@@ -183,2 +202,3 @@ couldExtend = true; | ||
if (backFirstTargetNode.parent === atRule.parent.parent) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n...tacking onto backFirstTargetNode :' + backFirstTargetNode); | ||
selectorRetainer = backFirstTargetNode.selectors; | ||
@@ -188,2 +208,3 @@ backFirstTargetNode.selector = uniqreq(selectorRetainer.concat(originSels)).join(', '); | ||
} else { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n...grabbing backFirstTargetNode :\n' + backFirstTargetNode); | ||
safeCopyDeclarations(backFirstTargetNode, atRule.parent); | ||
@@ -201,2 +222,3 @@ } | ||
//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); | ||
processExtension(atRule); | ||
@@ -208,2 +230,3 @@ couldExtend = true; | ||
//Use Tacking onto exiting selectors instead of new creation | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nUtilizing existing brother subclass for extension, as nothing matches: \n' + atRule.parent.selector + ' sub-' + extTgtSub); | ||
selectorRetainer = backFirstTargetNode.selectors; | ||
@@ -216,5 +239,7 @@ backFirstTargetNode.selector = uniqreq(selectorRetainer.concat(formSubSelector(originSels, extTgtSub))).join(', '); | ||
//utilize existing subclass for extension | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nUtilizing existing subclass for extension:\n' + subTarget.selector); | ||
safeCopyDeclarations(backFirstTargetNode, subTarget.node); | ||
} else { | ||
//create additional nodes below existing for each instance of subs | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nUtilizing new subclass for extension, as nothing matches: \n' + atRule.parent.selector + ' sub-' + extTgtSub); | ||
var newNode = postcss.rule(); | ||
@@ -234,9 +259,10 @@ newNode.semicolon = atRule.semicolon; | ||
if (!couldExtend) { | ||
result.warn('\'' + atRule.params + '\', has not been defined, so it cannot be extended'); | ||
result.warn('\'' + atRule.params + '\', has not been defined, so it cannot be extended', { node: atRule }); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n\'' + atRule.params + '\' has not been defined!!!'); | ||
} | ||
if (atRule.parent !== undefined) { | ||
if (!atRule.parent.nodes.length || atRule.parent.nodes.length === 1) { | ||
atRule.parent.removeSelf(); | ||
atRule.parent.remove(); | ||
} else { | ||
atRule.removeSelf(); | ||
atRule.remove(); | ||
} | ||
@@ -285,3 +311,4 @@ } | ||
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}); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nMEDIA2MEDIA extention detected, node :\n' + atRule.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: atRule}); | ||
return true; | ||
@@ -315,13 +342,15 @@ } | ||
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) { | ||
clone.before = node.before; | ||
clone.raws.before = node.raws.before; | ||
} else { | ||
clone.before = node.before + '\t'; | ||
clone.raws.before = node.raws.before + '\t'; | ||
} | ||
clone.after = node.after; | ||
clone.between = node.between; | ||
clone.raws.after = node.raws.after; | ||
clone.raws.between = node.raws.between; | ||
nodeDest.append(clone); | ||
@@ -359,3 +388,4 @@ }); | ||
//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}); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nANTIPATTERN CSS detected node :\n' + atRule.parent); | ||
result.warn('@extend is being used in an anti-pattern (extending things not yet defined). This is your first and final warning', {node: atRule}); | ||
isAntiPatternCSS = true; | ||
@@ -367,6 +397,9 @@ } | ||
if (recurseStack.indexOf(recursableRule.node.params) === -1) { | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nRecursing from ' + atRule.parent.selector + ' on: ' + recursableRule.node.parent + '\n\\/\\/\\/\\/\\/\\/\\/\\/ ' + recurseStack.length); | ||
processExtension(recursableRule.node); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n ^ ^ ^ ^ ^ ^ ^ ^ ' + recurseStack.length); | ||
recursableRule = findUnresolvedExtendChild(targetNode); | ||
} else { | ||
result.warn('Infinite extension 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 like this) before dropping | ||
@@ -378,4 +411,6 @@ recurseStack = uniqreq(recurseStack); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\npre-pop recurseStack : ' + recurseStack); | ||
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 }); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\n!!!!!!!!!!!!CRITICALLY MISALIGNED RECURSE STACK\nexpected : ' + atRule.params + '\npost-pop recurseStack : ' + recurseStack); | ||
} | ||
@@ -385,2 +420,3 @@ //Empty history if this is top of a recursion (as process preserves detections as it backs-out) | ||
recurseStack = []; | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nrecurseStack dumped, at top'); | ||
} | ||
@@ -403,2 +439,3 @@ return true; | ||
seldiff = uniqreq(seldiff); | ||
// /*DEBUG*/ appendout('./test/debugout.txt', '\nseldiff : ' + seldiff + '\n\tBetween:\n' + node.selectors + '\n\tand:\n' + selectorAccumulator); | ||
if (seldiff.length === selectorAccumulator.length) { | ||
@@ -405,0 +442,0 @@ foundNode = node; |
@@ -8,3 +8,3 @@ { | ||
"license": "MIT", | ||
"version": "0.4.3", | ||
"version": "1.0.0", | ||
"description": "As close to cssnext @extend as possible for PostCSS", | ||
@@ -36,3 +36,3 @@ "homepage": "https://github.com/travco/postcss-extend", | ||
"dependencies": { | ||
"postcss": "^4.1.0" | ||
"postcss": "^5.0.4" | ||
}, | ||
@@ -39,0 +39,0 @@ "devDependencies": { |
@@ -75,3 +75,3 @@ # postcss-extend [![Build Status](https://travis-ci.org/travco/postcss-extend.svg?branch=master)](https://travis-ci.org/travco/postcss-extend) | ||
With `@define-placeholder`, you associate a rule set with a placeholder selector, which you will later extend with concrete selectors. It (and its other aliases) can only be extended if it's already been declared in the document, and cannot be extended-out-of. | ||
With `@define-placeholder`, you associate a rule set with a placeholder selector, which you will later extend with concrete selectors. It (and its other aliases) can only be extended if it's already been declared in the document, and *cannot be extended-out-of*. | ||
@@ -98,3 +98,3 @@ You can also use it's aliases: `@define-extend` or `@extend-define`. | ||
Additionally, all definitions should only contain declarations and comments: no statements (violations should log warnings). | ||
Additionally, all definitions will log a warning if they go unused, and should only contain declarations and comments: no statements (violations will also log warnings). | ||
@@ -146,2 +146,5 @@ ### Extending Rules or Placeholders | ||
You also can still specifically extend subclasses by-themselves by calling them out explictly. | ||
If (in the above example) you wanted to only get `background:brown` instead of everything having to do with `.potato`, you could just use `@extend .potato:first-child;`. | ||
#### Extending with `@media` | ||
@@ -362,2 +365,11 @@ | ||
#### 'TLDR' Contention with the `@extend` [spec](https://tabatkins.github.io/specs/css-extend-rule): | ||
- **Order of Processing/Specificity** In normal cases, the document is processed top-to-bottom, however as a feature-fallout of the implementation, it is capable of extending in an anti-pattern (extending things yet to be declared). If what you're writing is an anti-pattern, it will throw a warning. | ||
- **Media-cross-media Inheritance** Attempting to extend a rule inside a media block from within another media block [is directly disallowed in the code](https://github.com/travco/postcss-extend#quirks) and will throw a warning. | ||
- **Silent placeholders** Includes both the stricter `@define-placeholder` and it's aliases for compatibility with [simple-extend](https://github.com/davidtheclark/postcss-simple-extend), and the `%` placeholder [from the spec](https://tabatkins.github.io/specs/css-extend-rule/#placeholder). As this isn't the native parser, the placeholder will be wiped from the CSS if it goes unused (as well as throw a warning). | ||
- **Subclass inheritance** Currently doesn't log a warning for it's use, as it is not stated in the spec for or against its behavior (despite it logically following). All [sub classes of an extended 'base' class are extended](https://github.com/travco/postcss-extend#extending-sub-classes-and-sub-elements), creating subclasses for the extending class as a means of mimicking the inheritance of specific sub-class contingencies (like `:active`) | ||
- **'Whiff' extension** trying to extend something that doesn't exist will log an error, and like everything else, remove the `@extend` rule. | ||
##### 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) |
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
42194
400
0
372
+ Addedansi-regex@2.1.1(transitive)
+ Addedansi-styles@2.2.1(transitive)
+ Addedchalk@1.1.3(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedhas-ansi@2.0.0(transitive)
+ Addedhas-flag@1.0.0(transitive)
+ Addedjs-base64@2.6.4(transitive)
+ Addedpostcss@5.2.18(transitive)
+ Addedsource-map@0.5.7(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedsupports-color@2.0.03.2.3(transitive)
- Removedamdefine@1.0.1(transitive)
- Removedes6-promise@2.3.0(transitive)
- Removedjs-base64@2.1.9(transitive)
- Removedpostcss@4.1.16(transitive)
- Removedsource-map@0.4.4(transitive)
Updatedpostcss@^5.0.4