123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- 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) {
- 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') {
- if (reverse) {
- ref.forEachRight(walk);
- } else {
- ref.forEach(walk);
- }
- } else {
- walk(ref);
- }
- }
- }
- 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
- }
- };
- }
- 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);
- var walk = function(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);
- };
- walk.find = function(ast, fn) {
- var found = null;
- walk(ast, function(node, item, list) {
- if (found === null && fn.call(this, node, item, list)) {
- found = node;
- }
- });
- return found;
- };
- walk.findLast = function(ast, fn) {
- var found = null;
- walk(ast, {
- reverse: true,
- enter: function(node, item, list) {
- if (found === null && fn.call(this, node, item, list)) {
- found = node;
- }
- }
- });
- 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;
- };
|