1'use strict'; 2 3var dirname = require('path').dirname; 4var constantinople = require('constantinople'); 5var walk = require('pug-walk'); 6var error = require('pug-error'); 7var runFilter = require('./run-filter'); 8 9module.exports = handleFilters; 10function handleFilters(ast, filters, options, filterAliases) { 11 options = options || {}; 12 walk(ast, function (node) { 13 var dir = node.filename ? dirname(node.filename) : null; 14 if (node.type === 'Filter') { 15 handleNestedFilters(node, filters, options, filterAliases); 16 var text = getBodyAsText(node); 17 var attrs = getAttributes(node, options); 18 attrs.filename = node.filename; 19 node.type = 'Text'; 20 node.val = filterWithFallback(node, text, attrs); 21 } else if (node.type === 'RawInclude' && node.filters.length) { 22 var firstFilter = node.filters.pop(); 23 var attrs = getAttributes(firstFilter, options); 24 var filename = attrs.filename = node.file.fullPath; 25 var str = node.file.str; 26 node.type = 'Text'; 27 node.val = filterFileWithFallback(firstFilter, filename, str, attrs); 28 node.filters.slice().reverse().forEach(function (filter) { 29 var attrs = getAttributes(filter, options); 30 attrs.filename = filename; 31 node.val = filterWithFallback(filter, node.val, attrs); 32 }); 33 node.filters = undefined; 34 node.file = undefined; 35 } 36 37 function filterWithFallback(filter, text, attrs, funcName) { 38 try { 39 var filterName = getFilterName(filter); 40 if (filters && filters[filterName]) { 41 return filters[filterName](text, attrs); 42 } else { 43 return runFilter(filterName, text, attrs, dir, funcName); 44 } 45 } catch (ex) { 46 if (ex.code === 'UNKNOWN_FILTER') { 47 throw error(ex.code, ex.message, filter); 48 } 49 throw ex; 50 } 51 } 52 53 function filterFileWithFallback(filter, filename, text, attrs) { 54 var filterName = getFilterName(filter); 55 if (filters && filters[filterName]) { 56 return filters[filterName](text, attrs); 57 } else { 58 return filterWithFallback(filter, filename, attrs, 'renderFile'); 59 } 60 } 61 }, {includeDependencies: true}); 62 function getFilterName(filter) { 63 var filterName = filter.name; 64 if (filterAliases && filterAliases[filterName]) { 65 filterName = filterAliases[filterName]; 66 if (filterAliases[filterName]) { 67 throw error( 68 'FILTER_ALISE_CHAIN', 69 'The filter "' + filter.name + '" is an alias for "' + filterName + 70 '", which is an alias for "' + filterAliases[filterName] + 71 '". Pug does not support chains of filter aliases.', 72 filter 73 ); 74 } 75 } 76 return filterName; 77 } 78 return ast; 79}; 80 81function handleNestedFilters(node, filters, options, filterAliases) { 82 if (node.block.nodes[0] && node.block.nodes[0].type === 'Filter') { 83 node.block.nodes[0] = handleFilters(node.block, filters, options, filterAliases).nodes[0]; 84 } 85} 86 87function getBodyAsText(node) { 88 return node.block.nodes.map( 89 function(node){ return node.val; } 90 ).join(''); 91} 92 93function getAttributes(node, options) { 94 var attrs = {}; 95 node.attrs.forEach(function (attr) { 96 try { 97 attrs[attr.name] = attr.val === true ? true : constantinople.toConstant(attr.val); 98 } catch (ex) { 99 if (/not constant/.test(ex.message)) { 100 throw error('FILTER_OPTION_NOT_CONSTANT', ex.message + ' All filters are rendered compile-time so filter options must be constants.', node); 101 } 102 throw ex; 103 } 104 }); 105 var opts = options[node.name] || {}; 106 Object.keys(opts).forEach(function (opt) { 107 if (!attrs.hasOwnProperty(opt)) { 108 attrs[opt] = opts[opt]; 109 } 110 }); 111 return attrs; 112} 113