| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 | 'use strict';module.exports = walkAST;function walkAST(ast, before, after, options) {  if (after && typeof after === 'object' && typeof options === 'undefined') {    options = after;    after = null;  }  options = options || {includeDependencies: false};  var parents = options.parents = options.parents || [];  var replace = function replace(replacement) {    if (Array.isArray(replacement) && !replace.arrayAllowed) {      throw new Error('replace() can only be called with an array if the last parent is a Block or NamedBlock');    }    ast = replacement;  };  replace.arrayAllowed = parents[0] && (    /^(Named)?Block$/.test(parents[0].type) ||    parents[0].type === 'RawInclude' && ast.type === 'IncludeFilter');  if (before) {    var result = before(ast, replace);    if (result === false) {      return ast;    } else if (Array.isArray(ast)) {      // return right here to skip after() call on array      return walkAndMergeNodes(ast);    }  }  parents.unshift(ast);  switch (ast.type) {    case 'NamedBlock':    case 'Block':      ast.nodes = walkAndMergeNodes(ast.nodes);      break;    case 'Case':    case 'Filter':    case 'Mixin':    case 'Tag':    case 'InterpolatedTag':    case 'When':    case 'Code':    case 'While':      if (ast.block) {        ast.block = walkAST(ast.block, before, after, options);      }      break;    case 'Each':      if (ast.block) {        ast.block = walkAST(ast.block, before, after, options);      }      if (ast.alternate) {        ast.alternate = walkAST(ast.alternate, before, after, options);      }      break;    case 'Conditional':      if (ast.consequent) {        ast.consequent = walkAST(ast.consequent, before, after, options);      }      if (ast.alternate) {        ast.alternate = walkAST(ast.alternate, before, after, options);      }      break;    case 'Include':      walkAST(ast.block, before, after, options);      walkAST(ast.file, before, after, options);      break;    case 'Extends':      walkAST(ast.file, before, after, options);      break;    case 'RawInclude':      ast.filters = walkAndMergeNodes(ast.filters);      walkAST(ast.file, before, after, options);      break;    case 'Attrs':    case 'BlockComment':    case 'Comment':    case 'Doctype':    case 'IncludeFilter':    case 'MixinBlock':    case 'YieldBlock':    case 'Text':      break;    case 'FileReference':      if (options.includeDependencies && ast.ast) {        walkAST(ast.ast, before, after, options);      }      break;    default:      throw new Error('Unexpected node type ' + ast.type);      break;  }  parents.shift();  after && after(ast, replace);  return ast;  function walkAndMergeNodes(nodes) {    return nodes.reduce(function (nodes, node) {      var result = walkAST(node, before, after, options);      if (Array.isArray(result)) {        return nodes.concat(result);      } else {        return nodes.concat([result]);      }    }, []);  }}
 |