| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 | var hasOwnProperty = Object.prototype.hasOwnProperty;var noop = function() {};function ensureFunction(value) {    return typeof value === 'function' ? value : noop;}function invokeForType(fn, type) {    return function(node, item, list) {        if (node.type === type) {            fn.call(this, node, item, list);        }    };}function getWalkersFromStructure(name, nodeType) {    var structure = nodeType.structure;    var walkers = [];    for (var key in structure) {        if (hasOwnProperty.call(structure, key) === false) {            continue;        }        var fieldTypes = structure[key];        var walker = {            name: key,            type: false,            nullable: false        };        if (!Array.isArray(structure[key])) {            fieldTypes = [structure[key]];        }        for (var i = 0; i < fieldTypes.length; i++) {            var fieldType = fieldTypes[i];            if (fieldType === null) {                walker.nullable = true;            } else if (typeof fieldType === 'string') {                walker.type = 'node';            } else if (Array.isArray(fieldType)) {                walker.type = 'list';            }        }        if (walker.type) {            walkers.push(walker);        }    }    if (walkers.length) {        return {            context: nodeType.walkContext,            fields: walkers        };    }    return null;}function getTypesFromConfig(config) {    var types = {};    for (var name in config.node) {        if (hasOwnProperty.call(config.node, name)) {            var nodeType = config.node[name];            if (!nodeType.structure) {                throw new Error('Missed `structure` field in `' + name + '` node type definition');            }            types[name] = getWalkersFromStructure(name, nodeType);        }    }    return types;}function createTypeIterator(config, reverse) {    var fields = config.fields.slice();    var contextName = config.context;    var useContext = typeof contextName === 'string';    if (reverse) {        fields.reverse();    }    return function(node, context, walk, walkReducer) {        var prevContextValue;        if (useContext) {            prevContextValue = context[contextName];            context[contextName] = node;        }        for (var i = 0; i < fields.length; i++) {            var field = fields[i];            var ref = node[field.name];            if (!field.nullable || ref) {                if (field.type === 'list') {                    var breakWalk = reverse                        ? ref.reduceRight(walkReducer, false)                        : ref.reduce(walkReducer, false);                    if (breakWalk) {                        return true;                    }                } else if (walk(ref)) {                    return true;                }            }        }        if (useContext) {            context[contextName] = prevContextValue;        }    };}function createFastTraveralMap(iterators) {    return {        Atrule: {            StyleSheet: iterators.StyleSheet,            Atrule: iterators.Atrule,            Rule: iterators.Rule,            Block: iterators.Block        },        Rule: {            StyleSheet: iterators.StyleSheet,            Atrule: iterators.Atrule,            Rule: iterators.Rule,            Block: iterators.Block        },        Declaration: {            StyleSheet: iterators.StyleSheet,            Atrule: iterators.Atrule,            Rule: iterators.Rule,            Block: iterators.Block,            DeclarationList: iterators.DeclarationList        }    };}module.exports = function createWalker(config) {    var types = getTypesFromConfig(config);    var iteratorsNatural = {};    var iteratorsReverse = {};    var breakWalk = Symbol('break-walk');    var skipNode = Symbol('skip-node');    for (var name in types) {        if (hasOwnProperty.call(types, name) && types[name] !== null) {            iteratorsNatural[name] = createTypeIterator(types[name], false);            iteratorsReverse[name] = createTypeIterator(types[name], true);        }    }    var fastTraversalIteratorsNatural = createFastTraveralMap(iteratorsNatural);    var fastTraversalIteratorsReverse = createFastTraveralMap(iteratorsReverse);    var walk = function(root, options) {        function walkNode(node, item, list) {            var enterRet = enter.call(context, node, item, list);            if (enterRet === breakWalk) {                debugger;                return true;            }            if (enterRet === skipNode) {                return false;            }            if (iterators.hasOwnProperty(node.type)) {                if (iterators[node.type](node, context, walkNode, walkReducer)) {                    return true;                }            }            if (leave.call(context, node, item, list) === breakWalk) {                return true;            }            return false;        }        var walkReducer = (ret, data, item, list) => ret || walkNode(data, item, list);        var enter = noop;        var leave = noop;        var iterators = iteratorsNatural;        var context = {            break: breakWalk,            skip: skipNode,            root: root,            stylesheet: null,            atrule: null,            atrulePrelude: null,            rule: null,            selector: null,            block: null,            declaration: null,            function: null        };        if (typeof options === 'function') {            enter = options;        } else if (options) {            enter = ensureFunction(options.enter);            leave = ensureFunction(options.leave);            if (options.reverse) {                iterators = iteratorsReverse;            }            if (options.visit) {                if (fastTraversalIteratorsNatural.hasOwnProperty(options.visit)) {                    iterators = options.reverse                        ? fastTraversalIteratorsReverse[options.visit]                        : fastTraversalIteratorsNatural[options.visit];                } else if (!types.hasOwnProperty(options.visit)) {                    throw new Error('Bad value `' + options.visit + '` for `visit` option (should be: ' + Object.keys(types).join(', ') + ')');                }                enter = invokeForType(enter, options.visit);                leave = invokeForType(leave, options.visit);            }        }        if (enter === noop && leave === noop) {            throw new Error('Neither `enter` nor `leave` walker handler is set or both aren\'t a function');        }        walkNode(root);    };    walk.break = breakWalk;    walk.skip = skipNode;    walk.find = function(ast, fn) {        var found = null;        walk(ast, function(node, item, list) {            if (fn.call(this, node, item, list)) {                found = node;                return breakWalk;            }        });        return found;    };    walk.findLast = function(ast, fn) {        var found = null;        walk(ast, {            reverse: true,            enter: function(node, item, list) {                if (fn.call(this, node, item, list)) {                    found = node;                    return breakWalk;                }            }        });        return found;    };    walk.findAll = function(ast, fn) {        var found = [];        walk(ast, function(node, item, list) {            if (fn.call(this, node, item, list)) {                found.push(node);            }        });        return found;    };    return walk;};
 |