diff --git a/lib/hexo/post.js b/lib/hexo/post.js index 1d9a6956cb..189c9d0f73 100644 --- a/lib/hexo/post.js +++ b/lib/hexo/post.js @@ -12,6 +12,14 @@ const yfm = require('hexo-front-matter'); const preservedKeys = ['title', 'slug', 'path', 'layout', 'date', 'content']; +const rPlaceholder = /(?:<|<)!--\uFFFC(\d+)--(?:>|>)/g; +const rSwigVar = /\{\{[\s\S]*?\}\}/g; +const rSwigComment = /\{#[\s\S]*?#\}/g; +const rSwigBlock = /\{%[\s\S]*?%\}/g; +const rSwigFullBlock = /\{% *(.+?)(?: *| +.*?)%\}[\s\S]+?\{% *end\1 *%\}/g; +const rSwigRawFullBlock = /{% *raw *%\}[\s\S]+?\{% *endraw *%\}/g; +const rSwigTagInsideInlineCode = /`.*{.*}.*`/g; + const _escapeContent = (cache, str) => { const placeholder = '\uFFFC'; return ``; @@ -27,7 +35,6 @@ class PostRenderCache { str = str.replace(//g, ''); } - const rPlaceholder = /(?:<|<)!--\uFFFC(\d+)--(?:>|>)/g; const restored = str.replace(rPlaceholder, (_, index) => { assert(this.cache[index]); const value = this.cache[index]; @@ -39,13 +46,12 @@ class PostRenderCache { } escapeAllSwigTags(str) { - const rSwigVar = /\{\{[\s\S]*?\}\}/g; - const rSwigComment = /\{#[\s\S]*?#\}/g; - const rSwigBlock = /\{%[\s\S]*?%\}/g; - const rSwigFullBlock = /\{% *(.+?)(?: *| +.*?)%\}[\s\S]+?\{% *end\1 *%\}/g; - const escape = _str => _escapeContent(this.cache, _str); - return str.replace(rSwigFullBlock, escape) + return str.replace(rSwigRawFullBlock, escape) // Escape {% raw %} first + .replace(rSwigTagInsideInlineCode, str => { + return str.replace(/{/g, '{').replace(/}/g, '}'); + }) + .replace(rSwigFullBlock, escape) .replace(rSwigBlock, escape) .replace(rSwigComment, '') .replace(rSwigVar, escape); @@ -97,7 +103,7 @@ class Post { const ctx = this.context; const { config } = ctx; - data.slug = slugize((data.slug || data.title).toString(), {transform: config.filename_case}); + data.slug = slugize((data.slug || data.title).toString(), { transform: config.filename_case }); data.layout = (data.layout || config.default_layout).toLowerCase(); data.date = data.date ? moment(data.date) : moment(); @@ -136,7 +142,7 @@ class Post { let yfmSplit; return this._getScaffold(data.layout).then(scaffold => { - const frontMatter = prepareFrontMatter({...data}); + const frontMatter = prepareFrontMatter({ ...data }); yfmSplit = yfm.split(scaffold); return tag.render(yfmSplit.data, frontMatter); @@ -184,7 +190,7 @@ class Post { const ctx = this.context; const { config } = ctx; const draftDir = join(ctx.source_dir, '_drafts'); - const slug = slugize(data.slug.toString(), {transform: config.filename_case}); + const slug = slugize(data.slug.toString(), { transform: config.filename_case }); data.slug = slug; const regex = new RegExp(`^${escapeRegExp(slug)}(?:[^\\/\\\\]+)`); let src = ''; @@ -252,7 +258,7 @@ class Post { data.content = content; // Run "before_post_render" filters - return ctx.execFilter('before_post_render', data, {context: ctx}); + return ctx.execFilter('before_post_render', data, { context: ctx }); }).then(() => { // Escape all Nunjucks/Swig tags if (!disableNunjucks) { @@ -284,7 +290,7 @@ class Post { data.content = content; // Run "after_post_render" filters - return ctx.execFilter('after_post_render', data, {context: ctx}); + return ctx.execFilter('after_post_render', data, { context: ctx }); }).asCallback(callback); } } diff --git a/test/scripts/hexo/post.js b/test/scripts/hexo/post.js index ecd7a388f7..33242bf74f 100644 --- a/test/scripts/hexo/post.js +++ b/test/scripts/hexo/post.js @@ -669,13 +669,13 @@ describe('Post', () => { }); it('render() - recover escaped nunjucks blocks which is html escaped', () => { - const content = '`{% raw %}{{ test }}{% endraw %}`'; + const content = '`{% raw %}{{ test }}{% endraw %}`, {%raw%}{{ test }}{%endraw%}'; return post.render(null, { content, engine: 'markdown' }).then(data => { - data.content.trim().should.eql('

{{ test }}

'); + data.content.trim().should.eql('

{{ test }}, {{ test }}

'); }); }); @@ -1004,4 +1004,29 @@ describe('Post', () => { data.content.trim().should.include('

Title 2

'); data.content.trim().should.include('

Title 3

'); }); + + // test for Issue #3259 + // https://github.com/hexojs/hexo/issues/3259 + it('render() - "{{" & "}}" inside inline code', async () => { + const content = 'In Go\'s templates, blocks look like this: `{{block "template name" .}} (content) {{end}}`.'; + + const data = await post.render(null, { + content, + engine: 'markdown' + }); + + data.content.trim().should.eql('

In Go’s templates, blocks look like this: &#123;&#123;block "template name" .&#125;&#125; (content) &#123;&#123;end&#125;&#125;.

'); + }); + + // test for https://github.com/hexojs/hexo/issues/3346#issuecomment-595497849 + it('render() - swig var inside inline code', async () => { + const content = '`{{ 1 + 1 }}` {{ 1 + 1 }}'; + + const data = await post.render(null, { + content, + engine: 'markdown' + }); + + data.content.trim().should.eql('

&#123;&#123; 1 + 1 &#125;&#125; 2

'); + }); });