Source: walkRules.js

/** @module */
import matches from "./matches.js";

/**
 * Recursively walk all "normal" rules, i.e. rules with selectors
 * @param rules {Object|Array} AST or array of CSS rules
 * @param callback {Function} Function to be executed for each matching rule. Rule passed as the only argument.
 * @param [test] {Object}
 * @param test.rules {string|RegExp|Function|Array} Which rules the callback runs on
 * @param test.type {string|RegExp|Function|Array} Which rule types the walker runs on
 * @param test.ancestors {string|RegExp|Function|Array} Which rules the walker descends on
 * @return The return value of the callback (which also breaks the loop) or undefined.
 */
export default function walkRules(rules, callback, test) {
	if (!rules) {
		return;
	}

	if (!Array.isArray(rules)) {
		// AST passed
		rules = rules.stylesheet.rules;
	}

	for (let rule of rules) {
		if (!test ||
		        matches(rule, test && test.rules)
		    &&  matches(rule.type, test && test.type)
		    && !matches(rule, test.not && test.not.rules, true)
		    && !matches(rule.type, test.not && test.not.type, true)
		) {
			let ret = callback(rule);

			if (ret !== undefined) {
				// Break loop and return immediately
				return ret;
			}
		}

		if (
		        matches(rule, test && test.ancestors)
		    && !matches(rule, test && test.not && test.not.ancestors, true)
		) {
			if (rule.rules) {
				walkRules(rule.rules, callback, test);
			}
		}
	}
}