Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter nesting failed in Jade

I have some filters:

var jade = require('jade');
jade.filters.Posts = function(block) {
    return '{block:Posts}'+jade.render(block)+'{/block:Posts}';
};
jade.filters.Audio = function(block) {
    return '{block:Audio}'+jade.render(block)+'{/block:Audio}';
};
jade.filters.Video = function(block) {
    return '{block:Video}'+jade.render(block)+'{/block:Video}';
};

And have some input

:Posts
    Posts
        :Audio
            | Audio
        :Video
            | Video

So I have an error:

 >> unknown filter ":Audio"

Can I handle or fix this problem?

PS You can look at the code in this repository — I'm using grunt and grunt-contrib-jade plugin, but to force grunt-contrib-jade work with filters you should edit ./node_modules/grunt-contrib-jade/tasks/jade.js to reflect changes from this pull request.

PS2: I found the stumbling block. When I use render() method inside filter, I invoke it from local jade instance, which is don't know anything about filters, but global jade instance (from Gruntfile.js) have all information about that filters. That's why the main question is: how can I throw global Jade-instance to file with filters?

PS3: I don't know how create fiddle for such case. But you can clone my Hampi repo, implement changes to grunt-contrib-jade from my PR to them, then at start run npm i. To compile templates run grunt jade. Pay attention to these line in body.jade and commented section in filters.

PS4. I find the reason and it in different scope. I describe it with details here. Can you solve this issue?


I'm open to additional answers and I will accept fixes in jade core (if it would be required).

like image 990
Vladimir Starkov Avatar asked Sep 15 '13 20:09

Vladimir Starkov


2 Answers

We just should bind global jade instance to filters like this:

var jade = require('jade');
if (options.filters) {
  // have custom filters
  Object.keys(options.filters).forEach(function(filter) {
    if (_.isFunction(data)) {
      // have custom options
      jade.filters[filter] = options.filters[filter].bind({jade: jade, locals: data()});
    } else {
      // have no custom options
      jade.filters[filter] = options.filters[filter].bind({jade: jade });
    }

  });
}

See implementation here — in this commit

like image 184
Vladimir Starkov Avatar answered Sep 21 '22 12:09

Vladimir Starkov


I think you are right at problem place, the problem is in the filter.js file

location jade/lib/filters.js

var transformers = require('transformers');

module.exports = filter;
function filter(name, str, options) {
  if (typeof filter[name] === 'function') {
    var res = filter[name](str, options);
  } else if (transformers[name]) {
    var res = transformers[name].renderSync(str, options);
    if (transformers[name].outputFormat === 'js') {
      res = '<script type="text/javascript">\n' + res + '</script>';
    } else if (transformers[name].outputFormat === 'css') {
      res = '<style type="text/css">' + res + '</style>';
    } else if (transformers[name].outputFormat === 'xml') {
      res = res.replace(/'/g, '&#39;');
    }
  } else {
    throw new Error('unknown filter ":' + name + '"');
  }
  return res; // returns RES that is not defined in scope of function.
}
filter.exists = function (name, str, options) {
  return typeof filter[name] === 'function' || transformers[name];
};

Here I have identified one flaw that you can correct like this,

var transformers = require('transformers');

module.exports = filter;
function filter(name, str, options) {
  var res;//defined a variable which is global to the scope of function.
  if (typeof filter[name] === 'function') {
    res = filter[name](str, options);
  } else if (transformers[name]) {
    res = transformers[name].renderSync(str, options);
    if (transformers[name].outputFormat === 'js') {
      res = '<script type="text/javascript">\n' + res + '</script>';
    } else if (transformers[name].outputFormat === 'css') {
      res = '<style type="text/css">' + res + '</style>';
    } else if (transformers[name].outputFormat === 'xml') {
      res = res.replace(/'/g, '&#39;');
    }
  } else {
    throw new Error('unknown filter ":' + name + '"');
  }
  return res;
}
filter.exists = function (name, str, options) {
  return typeof filter[name] === 'function' || transformers[name];
};

It may be possible that nesting under some function makes audio function out of scope. Does audio function works alone!?

although there may be other things if the problem not solved, please create a fiddle for your for better understanding.

like image 44
MarmiK Avatar answered Sep 18 '22 12:09

MarmiK