text: import postcss from 'postcss';
  import nesting from 'postcss-nesting';
  
  // functional selector match
  var functionalSelectorMatch = /(^|[^\w-])(%[_a-zA-Z]+[_a-zA-Z0-9-]*)([^\w-]|$)/i;
  
  // plugin
  var index = postcss.plugin('postcss-extend-rule', function (rawopts) {
  	// options ( onFunctionalSelector, onRecursiveExtend, onUnusedExtend)
  	var opts = Object(rawopts);
  	var extendMatch = opts.name instanceof RegExp ? opts.name : 'name' in opts ? new RegExp(`^${opts.name}$`, 'i') : 'extend';
  
  	return function (root, result) {
  		// for each extend at-rule
  		root.walkAtRules(extendMatch, function (extendAtRule) {
  			// do not revisit visited extend at-rules
  			if (!extendAtRule.__extendAtRuleVisited) {
  				extendAtRule.__extendAtRuleVisited = true;
  
  				// selector identifier
  				var selectorIdMatch = getSelectorIdMatch(extendAtRule.params);
  
  				// extending rules
  				var extendingRules = getExtendingRules(selectorIdMatch, extendAtRule);
  
  				// if there are extending rules
  				if (extendingRules.length) {
  					// replace the extend at-rule with the extending rules
  					extendAtRule.replaceWith(extendingRules);
  
  					// transform any nesting at-rules
  					extendingRules.forEach(function (extendingRule) {
  						transform(extendingRule);
  
  						extendingRule.walk(transform);
  					});
  				} else {
  					// manage unused extend at-rules
  					var unusedExtendMessage = `Unused extend at-rule "${extendAtRule.params}"`;
  
  					if (opts.onUnusedExtend === 'throw') {
  						throw extendAtRule.error(unusedExtendMessage, { word: extendAtRule.name });
  					} else if (opts.onUnusedExtend === 'warn') {
  						extendAtRule.warn(result, unusedExtendMessage);
  					} else if (opts.onUnusedExtend !== 'ignore') {
  						extendAtRule.remove();
  					}
  				}
  			} else {
  				// manage revisited extend at-rules
  				var revisitedExtendMessage = `Revisited extend at-rule "${extendAtRule.params}"`;
  
  				if (opts.onRecursiveExtend === 'throw') {
  					throw extendAtRule.error(revisitedExtendMessage, { word: extendAtRule.name });
  				} else if (opts.onRecursiveExtend === 'warn') {
  					extendAtRule.warn(result, revisitedExtendMessage);
  				} else if (opts.onRecursiveExtend !== 'ignore') {
  					extendAtRule.remove();
  				}
  			}
  		});
  
  		root.walkRules(functionalSelectorMatch, function (functionalRule) {
  			// manage encountered functional selectors
  			var functionalSelectorMessage = `Encountered functional selector "${functionalRule.selector}"`;
  
  			if (opts.onFunctionalSelector === 'throw') {
  				throw functionalRule.error(functionalSelectorMessage, { word: functionalRule.selector.match(functionalSelectorMatch)[1] });
  			} else if (opts.onFunctionalSelector === 'warn') {
  				functionalRule.warn(result, functionalSelectorMessage);
  			} else if (opts.onFunctionalSelector !== 'ignore') {
  				functionalRule.remove();
  			}
  		});
  	};
  });
  
  function transform(node) {
  	return nesting()({
  		walk(transformer) {
  			return transformer(node);
  		}
  	});
  }
  
  function getExtendingRules(selectorIdMatch, extendAtRule) {
  	// extending rules
  	var extendingRules = [];
  
  	// for each rule found from root of the extend at-rule with a matching selector identifier
  	extendAtRule.root().walkRules(selectorIdMatch, function (matchingRule) {
  		// nesting selectors for the selectors matching the selector identifier
  		var nestingSelectors = matchingRule.selectors.filter(function (selector) {
  			return selectorIdMatch.test(selector);
  		}).map(function (selector) {
  			return selector.replace(selectorIdMatch, '$1&$3');
  		}).join(',');
  
  		// matching rule’s cloned nodes
  		var nestingNodes = matchingRule.clone().nodes;
  
  		// clone the matching rule as a nested rule
  		var clone = extendAtRule.clone({
  			name: 'nest',
  			params: nestingSelectors,
  			nodes: nestingNodes,
  			// empty the extending rules, as they are likely non-comforming
  			raws: {}
  		});
  
  		// preserve nesting of parent rules and at-rules
  		var parent = matchingRule.parent;
  
  		while (parent && (parent.type === 'rule' || parent.type === 'atrule')) {
  			clone = parent.clone().removeAll().append([clone]);
  
  			parent = parent.parent;
  		}
  
  		// push the matching rule to the extending rules
  		extendingRules.push(clone);
  	});
  
  	// return the extending rules
  	return extendingRules;
  }
  
  function getSelectorIdMatch(selectorIds) {
  	// escape the contents of the selector id to avoid being parsed as regex
  	var escapedSelectorIds = postcss.list.comma(selectorIds).map(function (selectorId) {
  		return selectorId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  	}).join('|');
  
  	// selector unattached to an existing selector
  	var selectorIdMatch = new RegExp(`(^|[^\\w-])(${escapedSelectorIds})([^\\w-]|$)`, '');
  
  	return selectorIdMatch;
  }
  
  export default index;
  