2012-02-06 21 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>) 

快速和骯髒。可能不是最理想的,因爲它每次都會運行轉換,而不會緩存結果(我認爲),但至少它可以工作。

(編輯)

你也可以使用標記來呈現降價的瀏覽器,卸載從您的服務器的一些工作,使加載速度更快。