You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							216 lines
						
					
					
						
							6.0 KiB
						
					
					
				
			
		
		
	
	
							216 lines
						
					
					
						
							6.0 KiB
						
					
					
				'use strict'; | 
						|
 | 
						|
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 = reverse ? config.fields.slice().reverse() : config.fields; | 
						|
    var body = fields.map(function(field) { | 
						|
        var ref = 'node.' + field.name; | 
						|
        var line; | 
						|
 | 
						|
        if (field.type === 'list') { | 
						|
            line = reverse | 
						|
                ? ref + '.forEachRight(walk);' | 
						|
                : ref + '.forEach(walk);'; | 
						|
        } else { | 
						|
            line = 'walk(' + ref + ');'; | 
						|
        } | 
						|
 | 
						|
        if (field.nullable) { | 
						|
            line = 'if (' + ref + ') {\n    ' + line + '}'; | 
						|
        } | 
						|
 | 
						|
        return line; | 
						|
    }); | 
						|
 | 
						|
    if (config.context) { | 
						|
        body = [].concat( | 
						|
            'var old = context.' + config.context + ';', | 
						|
            'context.' + config.context + ' = node;', | 
						|
            body, | 
						|
            'context.' + config.context + ' = old;' | 
						|
        ); | 
						|
    } | 
						|
 | 
						|
    return new Function('node', 'context', 'walk', body.join('\n')); | 
						|
} | 
						|
 | 
						|
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 | 
						|
        } | 
						|
    }; | 
						|
} | 
						|
 | 
						|
module.exports = function createWalker(config) { | 
						|
    var types = getTypesFromConfig(config); | 
						|
    var iteratorsNatural = {}; | 
						|
    var iteratorsReverse = {}; | 
						|
 | 
						|
    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); | 
						|
 | 
						|
    return function walk(root, options) { | 
						|
        function walkNode(node, item, list) { | 
						|
            enter.call(context, node, item, list); | 
						|
 | 
						|
            if (iterators.hasOwnProperty(node.type)) { | 
						|
                iterators[node.type](node, context, walkNode); | 
						|
            } | 
						|
 | 
						|
            leave.call(context, node, item, list); | 
						|
        } | 
						|
 | 
						|
        var enter = noop; | 
						|
        var leave = noop; | 
						|
        var iterators = iteratorsNatural; | 
						|
        var context = { | 
						|
            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'); | 
						|
        } | 
						|
 | 
						|
        // swap handlers in reverse mode to invert visit order | 
						|
        if (options.reverse) { | 
						|
            var tmp = enter; | 
						|
            enter = leave; | 
						|
            leave = tmp; | 
						|
        } | 
						|
 | 
						|
        walkNode(root); | 
						|
    }; | 
						|
};
 | 
						|
 |