2012-02-06 70 views
2

尊敬的黑客,过滤Jade/Node.js中的Markdown内容

我希望过滤Jade模板中完全Markdown的字符串。 我在变量中有Markdown。

玉插值降价就好了里面的变量:

此:

var jade = require('jade'); 

var jade_string = [ 
    ':markdown', 
    ' ## This is markdown!', 
    ' * First', 
    ' * #{var2}', 
    ' * Third' 
].join('\n'); 

var fn = jade.compile(jade_string, { pretty: true }); 

console.log(fn({ var1: "First!", var2: "Second!" })); 

性导致的:

<h2>This is markdown!</h2> 

<ul> 
<li>First</li> 
<li>Second!</li> 
<li>Third</li> 
</ul> 

不过,我有什么是变量内实际完成降价。 这:

var jade = require('jade'); 

var jade_string = [ 
    'div.markedup', 
    ' :markdown', 
    '  \\#{var2}' 
].join('\n'); 

var fn = jade.compile(jade_string, { pretty: true }); 

var markdown = [ 
    '## I am marked!', 
    '* One', 
    '* Two' 
].join('\n'); 

console.log(fn({ var1: "First!", var2: markdown })); 

只能提供这样的:

<div class="markedup"><p>## I am marked! 
* One 
* Two</p> 
</div> 

因此,在我看来像玉石过滤之前的程序做任何变量 插,然后在出现的HTML插值变量。如果您希望在Markdown中编写模板,那么这是 ,但如果您要在Markdown中编写内容,那么 帮助不大。

我知道我可以用更多的编程解决这个问题,但我觉得我必须 缺少的东西。毕竟,将Markdown内容片段保存在 数据库中,并将生成的HTML片段填充到模板中,这似乎是:markdown过滤器最明显的用例。

在Jade中有没有“正常”的方法来做到这一点?

非常感谢提前等待启示。

回答

4

我认为答案是更多的编程,但我会告诉你我做了什么。我使用自定义中间件,让我在结束最终的HTML文档输出之前结合任意的转换过程。因此,例如,我在middleware.js模块中有以下过滤器,我将依次进行解释。

如此简单的视图只需使用普通的玉器及其各种滤镜进行降价,javascript,coffeescript。一些观点,例如博客文章,需要更复杂的中间件链,就像这样。

首先,根据请求,我建立了包含此响应核心内容的文件,并将其设置为res.viewPath上的属性。这可能是一个原始HTML片段文件或降价文件。然后我通过一系列中间件转换发送响应。我使用res.htmlres.dom来存储响应正在建立时的中间表示。

这只是存储原始HTML(只是一个没有头或布局的文档主体片段)。

html = function(req, res, next) { 
    if (!/\.html$/.test(res.viewPath)) return next(); 
    return fs.readFile(res.viewPath, "utf8", function(error, htmlText) { 
    res.html = htmlText; 
    return next(error); 
    }); 
}; 

这一个将markdown文件转换为HTML(使用markdown-js模块)。

markdownToHTML = function(req, res, next) { 
    if (!/\.md$/.test(res.viewPath)) return next(); 
    return fs.readFile(res.viewPath, "utf8", function(error, markdownText) { 
    res.html = markdown(markdownText); 
    return next(error); 
    }); 
}; 

我有一个子布局,在我的主布局内,但围绕每个博客文章。所以我把这篇博文贴在这里的子文件中。 (单独的代码未显示从json元数据文件生成res.post对象)。

blogArticle = function(req, res, next) { 
    var footerPath, post; 
    post = res.post; 
    footerPath = path.join(__dirname, "..", "templates", "blog_layout.jade"); 
    return fs.readFile(footerPath, "utf8", function(error, jadeText) { 
    var footerFunc; 
    if (error) return next(error); 
    footerFunc = jade.compile(jadeText); 
    res.html = footerFunc({ 
     post: post, 
     body: res.html 
    }); 
    return next(); 
    }); 
}; 

现在我围绕主要内容HTML包装我的布局。请注意,我可以在这里设置页面标题等内容,或者稍后再等待,因为我可以在此之后通过jsdom操作响应。我做body: res.html || "",所以我可以渲染一个空的布局,并在稍后插入主体,如果更方便。

exports.layout = function(req, res, next) { 
    var layoutPath; 
    layoutPath = path.join(__dirname, "..", "templates", "layout.jade"); 
    return fs.readFile(layoutPath, "utf8", function(error, jadeText) { 
    var layoutFunc, locals; 
    layoutFunc = jade.compile(jadeText, { 
     filename: layoutPath 
    }); 
    locals = { 
     config: config, 
     title: "", 
     body: res.html || "" 
    }; 
    res.html = layoutFunc(locals); 
    return next(error); 
    }); 
}; 

这里是真正强大的东西来。我将HTML字符串转换为jsdom文档对象模型,该模型允许在服务器端进行基于jQuery的转换。下面的toMarkup函数只是允许我在不添加额外的<script>标记的情况下获取HTML,这是我们在jsdom中添加的内存中的jquery。

exports.domify = function(req, res, next) { 
    return jsdom.env(res.html, [jqueryPath], function(error, dom) { 
    if (error) return next(error); 
    res.dom = dom; 
    dom.toMarkup = function() { 
     this.window.$("script").last().remove(); 
     return this.window.document.doctype + this.window.document.innerHTML; 
    }; 
    return next(error); 
    }); 
}; 

所以这里是我自定义的转换。这可以用一个真正有效的HTML代替一个制作好的DSL标签,如<flickrshow href="http://flickr.com/example"/>,否则将是一个非常讨厌的<object>样板文件,我不得不在每个博客文章中复制,如果flickr曾经更改过他们使用的样板标记,那将是一个维护痛苦去修复它在许多个人博客文章降价文件。他们当前使用的样板文件位于flickrshowTemplate变量中,并包含一个小胡子占位符{URLs}

exports.flickr = function(req, res, next) { 
    var $ = res.dom.window.$; 
    $("flickrshow").each(function(index, elem) { 
    var $elem, URLs; 
    $elem = $(elem); 
    URLs = $elem.attr("href"); 
    return $elem.replaceWith(flickrshowTemplate.replace(/\{URLs\}/g, URLs)); 
    }); 
    return next(); 
}; 

同上嵌入youtube视频。 <youtube href="http://youtube.com/example"/>

exports.youtube = function(req, res, next) { 
    var $ = res.dom.window.$; 
    $("youtube").each(function(index, elem) { 
    var $elem, URL; 
    $elem = $(elem); 
    URL = $elem.attr("href"); 
    return $elem.replaceWith(youtubeTemplate.replace(/\{URL\}/, URL)); 
    }); 
    return next(); 
}; 

现在我可以更改标题,如果我想,添加/删除的JavaScript或样式表等。在这里我设置标题的布局已经呈现后。

postTitle = function(req, res, next) { 
    var $; 
    $ = res.dom.window.$; 
    $("title").text(res.post.title + " | Peter Lyons"); 
    return next(); 
}; 

好吧,回到最终的HTML的时候了。

exports.undomify = function(req, res, next) { 
    res.html = res.dom.toMarkup(); 
    return next(); 
}; 

现在我们推出它!

exports.send = function(req, res) { 
    return res.send(res.html); 
}; 

,以便绑在一起,这一切,并有明确的使用它,我们做

postMiddleware = [ 
    loadPost, 
    html, 
    markdownToHTML, 
    blogArticle, 
    layout, 
    domify, 
    postTitle, 
    flickr, 
    youtube, 
    undomify, 
    send 
] 

app.get("/your/uri", postMiddleware); 

简明?没有清洁?我想是这样。灵活?非常。非常快速?可能不会很快,因为我相信jsdom是你可以做的更重量级的事情之一,但是我将它用作静态站点生成器,因此速度无关紧要。当然,在中间件链的开始和结尾处添加另一个函数以将最终的HTML写入静态文件并在其比对应的降格页面主体内容文件更新时直接提供它将是微不足道的。 Stackoverflowers,我很乐意听到关于这种方法的想法和建议!

+0

这很有趣,谢谢。我实际上并没有考虑如何让它达到这个目标,但是如果我的“节点实验”使它超过了原型阶段,这是非常好的思考。性能问题(如果有的话)可以通过将整个东西编译成一个函数来缓解,如Jade所做的那样,并缓存该函数。 – 2012-02-06 18:55:32

2

玉人说变量传递给过滤器is not supported。在玉

marked = require 'marked' 
marked.setOptions 
    <my options> 
app.locals.md = marked 

然后:我无法通过彼得里昂的回答得到的,所以我用这个

!= md(<markdown string>) 

快速和肮脏。可能不是最理想的,因为它每次都会运行转换,而不会缓存结果(我认为),但至少它可以工作。

(编辑)

你也可以使用标记来呈现降价的浏览器,卸载从您的服务器的一些工作,使加载速度更快。