From 7dd60284e4ad43ac31040995ddc70fbba9fa8896 Mon Sep 17 00:00:00 2001 From: Crphang Date: Thu, 20 Feb 2020 22:02:39 +0800 Subject: [PATCH 1/3] Allow nunjucks that is called multiple times to support escaping for special tags --- src/Page.js | 35 ++++++----- src/Site.js | 3 +- src/lib/markbind/src/parser.js | 61 ++++++++++++------- .../preprocessors/componentPreprocessor.js | 7 ++- src/lib/markbind/src/utils/nunjuckUtils.js | 31 ++++++++++ test/functional/test_site/expected/index.html | 5 ++ test/functional/test_site/index.md | 4 ++ test/functional/test_site/testNunjuckUtils.md | 7 +++ .../_markbind/plugins/testSpecialTag.js | 17 +++++- .../expected/index.html | 8 +++ .../test_site_special_tags/index.md | 9 +++ test/unit/nunjuckUtils.test.js | 36 +++++++++++ 12 files changed, 181 insertions(+), 42 deletions(-) create mode 100644 src/lib/markbind/src/utils/nunjuckUtils.js create mode 100644 test/functional/test_site/testNunjuckUtils.md create mode 100644 test/unit/nunjuckUtils.test.js diff --git a/src/Page.js b/src/Page.js index 37d44e5294..4ccb77c74d 100644 --- a/src/Page.js +++ b/src/Page.js @@ -6,6 +6,7 @@ const nunjucks = require('nunjucks'); const path = require('path'); const pathIsInside = require('path-is-inside'); const Promise = require('bluebird'); +const nunjuckUtils = require('./lib/markbind/src/utils/nunjuckUtils'); const _ = {}; _.isString = require('lodash/isString'); @@ -440,7 +441,7 @@ class Page { // Retrieve Expressive Layouts page and insert content fs.readFileAsync(layoutPagePath, 'utf8') .then(result => markbinder.includeData(layoutPagePath, result, layoutFileConfig)) - .then(result => nj.renderString(result, template)) + .then(result => nunjuckUtils.renderEscaped(nj, result, template)) .then((result) => { this.collectIncludedFiles(markbinder.getDynamicIncludeSrc()); this.collectIncludedFiles(markbinder.getStaticIncludeSrc()); @@ -481,7 +482,7 @@ class Page { // Map variables const newBaseUrl = Page.calculateNewBaseUrl(this.sourcePath, this.rootPath, this.baseUrlMap) || ''; const userDefinedVariables = this.userDefinedVariablesMap[path.join(this.rootPath, newBaseUrl)]; - return `${nunjucks.renderString(headerContent, userDefinedVariables)}\n${pageData}`; + return `${nunjuckUtils.renderEscaped(nunjucks, headerContent, userDefinedVariables)}\n${pageData}`; } /** @@ -511,7 +512,7 @@ class Page { // Map variables const newBaseUrl = Page.calculateNewBaseUrl(this.sourcePath, this.rootPath, this.baseUrlMap) || ''; const userDefinedVariables = this.userDefinedVariablesMap[path.join(this.rootPath, newBaseUrl)]; - return `${pageData}\n${nunjucks.renderString(footerContent, userDefinedVariables)}`; + return `${pageData}\n${nunjuckUtils.renderEscaped(nunjucks, footerContent, userDefinedVariables)}`; } /** @@ -548,7 +549,7 @@ class Page { // Map variables const newBaseUrl = Page.calculateNewBaseUrl(this.sourcePath, this.rootPath, this.baseUrlMap) || ''; const userDefinedVariables = this.userDefinedVariablesMap[path.join(this.rootPath, newBaseUrl)]; - const siteNavMappedData = nunjucks.renderString(siteNavContent, userDefinedVariables); + const siteNavMappedData = nunjuckUtils.renderEscaped(nunjucks, siteNavContent, userDefinedVariables); // Convert to HTML const siteNavDataSelector = cheerio.load(siteNavMappedData); if (siteNavDataSelector('navigation').length > 1) { @@ -698,12 +699,12 @@ class Page { // Map variables const newBaseUrl = Page.calculateNewBaseUrl(this.sourcePath, this.rootPath, this.baseUrlMap) || ''; const userDefinedVariables = this.userDefinedVariablesMap[path.join(this.rootPath, newBaseUrl)]; - const headFileMappedData = nunjucks.renderString(headFileContent, userDefinedVariables) + const headFileMappedData = nunjuckUtils.renderEscaped(nunjucks, headFileContent, userDefinedVariables) .trim(); // Split top and bottom contents const $ = cheerio.load(headFileMappedData, { xmlMode: false }); if ($('head-top').length) { - collectedTopContent.push(nunjucks.renderString($('head-top') + collectedTopContent.push(nunjuckUtils.renderEscaped(nunjucks, $('head-top') .html(), { baseUrl, hostBaseUrl, @@ -714,7 +715,7 @@ class Page { $('head-top') .remove(); } - collectedBottomContent.push(nunjucks.renderString($.html(), { + collectedBottomContent.push(nunjuckUtils.renderEscaped(nunjucks, $.html(), { baseUrl, hostBaseUrl, }) @@ -798,6 +799,7 @@ class Page { .then(result => this.insertFooterFile(result)) .then(result => Page.insertTemporaryStyles(result)) .then(result => markbinder.resolveBaseUrl(result, fileConfig)) + .then(result => nunjuckUtils.removeNunjucksEscapes(result)) .then(result => fs.outputFileAsync(this.tempPath, result)) .then(() => markbinder.renderFile(this.tempPath, fileConfig)) .then(result => this.postRender(result)) @@ -814,10 +816,11 @@ class Page { this.addLayoutFiles(); this.collectHeadFiles(baseUrl, hostBaseUrl); - this.content = nunjucks.renderString(this.content, { - baseUrl, - hostBaseUrl, - }); + this.content = nunjucks.renderString(this.content, + { + baseUrl, + hostBaseUrl, + }); this.collectAllPageSections(); this.buildPageNav(); @@ -1063,6 +1066,7 @@ class Page { isDynamic: true, dynamicSource: source, })) + .then(result => nunjuckUtils.removeNunjucksEscapes(result)) .then(result => fs.outputFileAsync(tempPath, result)) .then(() => markbinder.renderFile(tempPath, { baseUrlMap: this.baseUrlMap, @@ -1077,10 +1081,11 @@ class Page { const newBaseUrl = Page.calculateNewBaseUrl(file, this.rootPath, this.baseUrlMap); const baseUrl = newBaseUrl ? `${this.baseUrl}/${newBaseUrl}` : this.baseUrl; const hostBaseUrl = this.baseUrl; - const content = nunjucks.renderString(result, { - baseUrl, - hostBaseUrl, - }); + const content = nunjucks.renderString(result, + { + baseUrl, + hostBaseUrl, + }); return fs.outputFileAsync(resultPath, htmlBeautify(content, Page.htmlBeautifyOptions)); }) .then(() => { diff --git a/src/Site.js b/src/Site.js index 916cf0b9d7..ffe3470b8f 100644 --- a/src/Site.js +++ b/src/Site.js @@ -8,6 +8,7 @@ const Promise = require('bluebird'); const ProgressBar = require('progress'); const walkSync = require('walk-sync'); const MarkBind = require('./lib/markbind/src/parser'); +const nunjuckUtils = require('./lib/markbind/src/utils/nunjuckUtils'); const injectHtmlParser2SpecialTags = require('./lib/markbind/src/patches/htmlparser2'); const injectMarkdownItSpecialTags = require( './lib/markbind/src/lib/markdown-it-shared/markdown-it-escape-special-tags'); @@ -521,7 +522,7 @@ class Site { $('variable,span').each(function () { const name = $(this).attr('name') || $(this).attr('id'); // Process the content of the variable with nunjucks, in case it refers to other variables. - const html = nunjucks.renderString($(this).html(), userDefinedVariables); + const html = nunjuckUtils.renderEscaped(nunjucks, $(this).html(), userDefinedVariables); userDefinedVariables[name] = html; }); }); diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js index baae95cb46..2bf7538b7f 100644 --- a/src/lib/markbind/src/parser.js +++ b/src/lib/markbind/src/parser.js @@ -7,6 +7,7 @@ const Promise = require('bluebird'); const slugify = require('@sindresorhus/slugify'); const componentParser = require('./parsers/componentParser'); const componentPreprocessor = require('./preprocessors/componentPreprocessor'); +const nunjuckUtils = require('./utils/nunjuckUtils'); const _ = {}; _.clone = require('lodash/clone'); @@ -100,7 +101,7 @@ class Parser { return; } if (!pageVariables[variableName]) { - const variableValue = nunjucks.renderString(md.renderInline(variableElement.html()), { + const variableValue = nunjuckUtils.renderEscaped(nunjucks, md.renderInline(variableElement.html()), { ...importedVariables, ...pageVariables, ...userDefinedVariables, ...includedVariables, }); pageVariables[variableName] = variableValue; @@ -145,9 +146,10 @@ class Parser { // Extract page variables from the CHILD file const pageVariables = this.extractPageVariables(asIfAt, fileContent, userDefinedVariables, includeVariables); - const content = nunjucks.renderString(fileContent, - { ...pageVariables, ...includeVariables, ...userDefinedVariables }, - { path: filePath }); + const content + = nunjuckUtils.renderEscaped(nunjucks, fileContent, + { ...pageVariables, ...includeVariables, ...userDefinedVariables }, + { path: filePath }); const childContext = _.cloneDeep(context); childContext.cwf = asIfAt; childContext.variables = includeVariables; @@ -180,8 +182,11 @@ class Parser { = this._renderIncludeFile(filePath, node, context, config); this.extractInnerVariablesIfNotProcessed(renderedContent, childContext, config, filePath); const innerVariables = this.getImportedVariableMap(filePath); + Parser.VARIABLE_LOOKUP.get(filePath).forEach((value, variableName, map) => { - map.set(variableName, nunjucks.renderString(value, { ...userDefinedVariables, ...innerVariables })); + map.set(variableName, nunjuckUtils.renderEscaped(nunjucks, + value, + { ...userDefinedVariables, ...innerVariables })); }); }); } @@ -392,13 +397,20 @@ class Parser { const userDefinedVariables = config.userDefinedVariablesMap[path.resolve(parent, relative)]; const pageVariables = this.extractPageVariables(file, data, userDefinedVariables, {}); let fileContent - = nunjucks.renderString( - data, - { ...pageVariables, ...userDefinedVariables }, - { path: actualFilePath }); + = nunjuckUtils.renderEscaped(nunjucks, + data, + { + ...pageVariables, + ...userDefinedVariables, + }, + { + path: actualFilePath, + }); this._extractInnerVariables(fileContent, context, config); const innerVariables = this.getImportedVariableMap(context.cwf); - fileContent = nunjucks.renderString(fileContent, { ...userDefinedVariables, ...innerVariables }); + fileContent = nunjuckUtils.renderEscaped(nunjucks, + fileContent, + { ...userDefinedVariables, ...innerVariables }); const fileExt = utils.getExt(file); if (utils.isMarkdownFileExt(fileExt)) { context.source = 'md'; @@ -461,20 +473,23 @@ class Parser { const { additionalVariables } = config; const pageVariables = this.extractPageVariables(actualFilePath, pageData, userDefinedVariables, {}); - let fileContent = nunjucks.renderString(pageData, - { - ...pageVariables, - ...userDefinedVariables, - ...additionalVariables, - }, - { path: actualFilePath }); + let fileContent = nunjuckUtils.renderEscaped(nunjucks, + pageData, + { + ...pageVariables, + ...userDefinedVariables, + ...additionalVariables, + }, + { path: actualFilePath }); this._extractInnerVariables(fileContent, currentContext, config); const innerVariables = this.getImportedVariableMap(currentContext.cwf); - fileContent = nunjucks.renderString(fileContent, { - ...userDefinedVariables, - ...additionalVariables, - ...innerVariables, - }); + fileContent = nunjuckUtils.renderEscaped(nunjucks, + fileContent, + { + ...userDefinedVariables, + ...additionalVariables, + ...innerVariables, + }); const fileExt = utils.getExt(actualFilePath); if (utils.isMarkdownFileExt(fileExt)) { currentContext.source = 'md'; @@ -613,7 +628,7 @@ class Parser { this.rootPath, this.baseUrlMap); if (currentBase && currentBase.relative !== newBaseUrl) { cheerio.prototype.options.xmlMode = false; - const rendered = nunjucks.renderString(cheerio.html(node.children), { + const rendered = nunjuckUtils.renderEscaped(nunjucks, cheerio.html(node.children), { // This is to prevent the nunjuck call from converting {{hostBaseUrl}} to an empty string // and let the hostBaseUrl value be injected later. hostBaseUrl: '{{hostBaseUrl}}', diff --git a/src/lib/markbind/src/preprocessors/componentPreprocessor.js b/src/lib/markbind/src/preprocessors/componentPreprocessor.js index 7e3e642a33..9403996194 100644 --- a/src/lib/markbind/src/preprocessors/componentPreprocessor.js +++ b/src/lib/markbind/src/preprocessors/componentPreprocessor.js @@ -8,6 +8,7 @@ const CyclicReferenceError = require('../handlers/cyclicReferenceError.js'); const md = require('../lib/markdown-it'); const utils = require('../utils'); const urlUtils = require('../utils/urls'); +const nunjuckUtils = require('../utils/nunjuckUtils'); const _ = {}; _.has = require('lodash/has'); @@ -200,7 +201,7 @@ function _rebaseReferenceForStaticIncludes(pageData, element, config) { const newBase = fileBase.relative; const newBaseUrl = `{{hostBaseUrl}}/${newBase}`; - return nunjucks.renderString(pageData, { baseUrl: newBaseUrl }, { path: filePath }); + return nunjuckUtils.renderEscaped(nunjucks, pageData, { baseUrl: newBaseUrl }, { path: filePath }); } function _deleteIncludeAttributes(node) { @@ -317,7 +318,9 @@ function _preprocessInclude(node, context, config, parser) { parser.extractInnerVariablesIfNotProcessed(content, childContext, config, filePath); const innerVariables = parser.getImportedVariableMap(filePath); - const fileContent = nunjucks.renderString(content, { ...userDefinedVariables, ...innerVariables }); + const fileContent = nunjuckUtils.renderEscaped(nunjucks, + content, + { ...userDefinedVariables, ...innerVariables }); _deleteIncludeAttributes(element); diff --git a/src/lib/markbind/src/utils/nunjuckUtils.js b/src/lib/markbind/src/utils/nunjuckUtils.js new file mode 100644 index 0000000000..28491b99ee --- /dev/null +++ b/src/lib/markbind/src/utils/nunjuckUtils.js @@ -0,0 +1,31 @@ +const START_ESCAPE_STR = '{% raw %}'; +const END_ESCAPE_STR = '{% endraw %}'; +const REGEX = new RegExp('{% *raw *%}(.*?){% *endraw *%}', 'gs'); // eslint-disable-line no-useless-escape + +function addEscapeTags(match) { + return `${START_ESCAPE_STR}${match}${END_ESCAPE_STR}`; +} + +function removeEscapeTags(_, p1) { + return p1; +} + +function preEscapeRawTags(pageData) { + return pageData.replace(REGEX, addEscapeTags); +} + +module.exports = { + renderEscaped(nunjucks, pageData, variableMap = {}, options = {}) { + const escapedPage = preEscapeRawTags(pageData); + return nunjucks.renderString(escapedPage, variableMap, options); + }, + + /** + * RemoveNunjucksEscapes removes raw tags from page data when processing + * Downstream calls to nunjuckUtils.renderEscaped will be essentially the same as nunjucks.renderString + * This ensures that we remove {{ and }} from the final output which prevents problem with Vue + */ + removeNunjucksEscapes(pageData) { + return pageData.replace(REGEX, removeEscapeTags); + }, +}; diff --git a/test/functional/test_site/expected/index.html b/test/functional/test_site/expected/index.html index 71337fa7cc..0ad10605d7 100644 --- a/test/functional/test_site/expected/index.html +++ b/test/functional/test_site/expected/index.html @@ -490,6 +490,11 @@

Markbind Plugin Pre-renderLevel 2 header (inside headingSearchIndex) with no-index attribute should not be indexed

Level 6 header (outside headingSearchIndex) with always-index attribute should be indexed
+

Test NunjuckUtils

+
+

The standard trick to render braces still works {{ content }}.

+

Nonexistent variables should still be removed: . In the rare event that user wrapped an unknown variable inside a raw tag, it should retain its current behaviour and not be produced to ensure Vue does not break.

+

    diff --git a/test/functional/test_site/index.md b/test/functional/test_site/index.md index 8821f50a97..cebe9793b0 100644 --- a/test/functional/test_site/index.md +++ b/test/functional/test_site/index.md @@ -285,3 +285,7 @@ tags: ["tag-frontmatter-shown", "tag-included-file", "+tag-exp*", "-tag-exp-hidd ## Level 2 header (inside headingSearchIndex) with no-index attribute should not be indexed {.no-index} ###### Level 6 header (outside headingSearchIndex) with always-index attribute should be indexed {.always-index} + +**Test NunjuckUtils** + + \ No newline at end of file diff --git a/test/functional/test_site/testNunjuckUtils.md b/test/functional/test_site/testNunjuckUtils.md new file mode 100644 index 0000000000..164b365d2a --- /dev/null +++ b/test/functional/test_site/testNunjuckUtils.md @@ -0,0 +1,7 @@ +{%raw%} + +The standard trick to render braces still works {{ content }}. + +Nonexistent variables should still be removed: {{ unknown_variable }}. In the rare event that user wrapped an unknown variable inside a raw tag, it should retain its current behaviour and not be produced to ensure Vue does not break. + +{%endraw%} \ No newline at end of file diff --git a/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js b/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js index 1ff4dc3b5b..65953d8828 100644 --- a/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js +++ b/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js @@ -1,5 +1,11 @@ const cheerio = module.parent.require('cheerio'); +const ESCAPE_REGEX = new RegExp('{% *raw *%}(.*?){% *endraw *%}', 'gs'); + +function removeEscapeTags(_, p1) { + return p1; +} + /* Simple test plugin that whitelists as a special tag. If encountered, it wraps the text node inside with some indication text as to @@ -17,11 +23,20 @@ function preRender(content) { $(testElement).text(wrappedText); }); + const escapedNunjucks = $('mustache'); + escapedNunjucks.each((index, element) => { + const unwrappedText = $(element).text(); + const unescapedText = unwrappedText.replace(ESCAPE_REGEX, removeEscapeTags); + const transformedText = unescapedText.replace(/{/g, '!success').replace(/}/g, 'success!'); + $(element).text(transformedText); + }); + + return $.html(); } module.exports = { preRender, - getSpecialTags: () => ['testtag'], + getSpecialTags: () => ['testtag', 'mustache'], }; diff --git a/test/functional/test_site_special_tags/expected/index.html b/test/functional/test_site_special_tags/expected/index.html index 888b6ca0ae..a6c7719cf3 100644 --- a/test/functional/test_site_special_tags/expected/index.html +++ b/test/functional/test_site_special_tags/expected/index.html @@ -42,6 +42,14 @@

    So far as to comply with t

    There should be text between this and the next <hr> tag, since it is a special tag. All text should appear in the browser window as a single line, save for the comment which the browser still interprets. (but will be in the expected output)

    + + + +!success!success This should be enclosed in success string success!success! + +!success This should also be enclosed in success strings success!success!success! + + !success diff --git a/test/functional/test_site_special_tags/index.md b/test/functional/test_site_special_tags/index.md index 724e85ea09..3dc7adf80e 100644 --- a/test/functional/test_site_special_tags/index.md +++ b/test/functional/test_site_special_tags/index.md @@ -29,6 +29,15 @@ There should be text between this and the next `
    ` tag, since it is a special All text should appear in the browser window as a single line, save for the comment which the browser still interprets. (but will be in the expected output) + +{%raw%} + +{{ This should be enclosed in success string }} + +{ This should also be enclosed in success strings }}} +{%endraw%} + + some text diff --git a/test/unit/nunjuckUtils.test.js b/test/unit/nunjuckUtils.test.js new file mode 100644 index 0000000000..4bf9b9c138 --- /dev/null +++ b/test/unit/nunjuckUtils.test.js @@ -0,0 +1,36 @@ +const nunjucks = require('nunjucks'); +const nunjuckUtils = require('../../src/lib/markbind/src/utils/nunjuckUtils'); + +test('Escaping nunjucks raw tags', () => { + const escapedString = 'This is a content with escaped data {%raw%} CONTENT {%endraw%}'; + const escapedContent = nunjuckUtils.renderEscaped(nunjucks, escapedString); + + expect(escapedContent).toBe(escapedString); +}); + +test('Escaping nunjucks with new lines', () => { + const escapedString = 'This is a content with escaped data\n {%raw%} \nCONTENT\n {%endraw%}'; + const escapedContent = nunjuckUtils.renderEscaped(nunjucks, escapedString); + + expect(escapedContent).toBe(escapedString); +}); + +test('Escaping multiple nunjucks raw tags', () => { + const escapedString = 'Multiple escapes: {%raw%} first {%endraw%} {%raw%} second {%endraw%}'; + const escapedContent = nunjuckUtils.renderEscaped(nunjucks, escapedString); + + expect(escapedContent).toBe(escapedString); +}); + +test('Escaping nested nunjucks raw tags', () => { + const escapedString = 'Nested escaped data: {% raw %} {% raw %} CONTENT {% endraw %}{% endraw %}'; + const escapedContent = nunjuckUtils.renderEscaped(nunjucks, escapedString); + expect(escapedContent).toBe(escapedString); +}); + +test('Removing Nunjucks Raw Tags', () => { + const escapedString = 'This is a content with escaped data {%raw%} CONTENT {%endraw%}'; + const removedEscapedString = nunjuckUtils.removeNunjucksEscapes(escapedString); + const expectedRemove = 'This is a content with escaped data CONTENT '; + expect(removedEscapedString).toBe(expectedRemove); +}); From 06070b2524a32c2193e6598e91818a6e2365c5ac Mon Sep 17 00:00:00 2001 From: Crphang Date: Wed, 11 Mar 2020 19:34:07 +0800 Subject: [PATCH 2/3] Remove the need to prune raw tags, simplify logic of replacement --- src/Page.js | 2 -- src/lib/markbind/src/parser.js | 12 +++++++---- src/lib/markbind/src/utils/nunjuckUtils.js | 21 ++----------------- test/functional/test_site/expected/index.html | 5 ----- test/functional/test_site/index.md | 4 ---- test/functional/test_site/testNunjuckUtils.md | 7 ------- .../_markbind/plugins/testSpecialTag.js | 18 ++++++++++------ .../expected/index.html | 8 +++---- test/unit/nunjuckUtils.test.js | 7 ------- 9 files changed, 26 insertions(+), 58 deletions(-) delete mode 100644 test/functional/test_site/testNunjuckUtils.md diff --git a/src/Page.js b/src/Page.js index 4ccb77c74d..a3aba30d36 100644 --- a/src/Page.js +++ b/src/Page.js @@ -799,7 +799,6 @@ class Page { .then(result => this.insertFooterFile(result)) .then(result => Page.insertTemporaryStyles(result)) .then(result => markbinder.resolveBaseUrl(result, fileConfig)) - .then(result => nunjuckUtils.removeNunjucksEscapes(result)) .then(result => fs.outputFileAsync(this.tempPath, result)) .then(() => markbinder.renderFile(this.tempPath, fileConfig)) .then(result => this.postRender(result)) @@ -1066,7 +1065,6 @@ class Page { isDynamic: true, dynamicSource: source, })) - .then(result => nunjuckUtils.removeNunjucksEscapes(result)) .then(result => fs.outputFileAsync(tempPath, result)) .then(() => markbinder.renderFile(tempPath, { baseUrlMap: this.baseUrlMap, diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js index 2bf7538b7f..21015714ec 100644 --- a/src/lib/markbind/src/parser.js +++ b/src/lib/markbind/src/parser.js @@ -146,10 +146,14 @@ class Parser { // Extract page variables from the CHILD file const pageVariables = this.extractPageVariables(asIfAt, fileContent, userDefinedVariables, includeVariables); - const content - = nunjuckUtils.renderEscaped(nunjucks, fileContent, - { ...pageVariables, ...includeVariables, ...userDefinedVariables }, - { path: filePath }); + const content = nunjuckUtils.renderEscaped(nunjucks, + fileContent, + { + ...pageVariables, + ...includeVariables, + ...userDefinedVariables, + }, + { path: filePath }); const childContext = _.cloneDeep(context); childContext.cwf = asIfAt; childContext.variables = includeVariables; diff --git a/src/lib/markbind/src/utils/nunjuckUtils.js b/src/lib/markbind/src/utils/nunjuckUtils.js index 28491b99ee..222a4c3a6a 100644 --- a/src/lib/markbind/src/utils/nunjuckUtils.js +++ b/src/lib/markbind/src/utils/nunjuckUtils.js @@ -1,17 +1,9 @@ const START_ESCAPE_STR = '{% raw %}'; const END_ESCAPE_STR = '{% endraw %}'; -const REGEX = new RegExp('{% *raw *%}(.*?){% *endraw *%}', 'gs'); // eslint-disable-line no-useless-escape - -function addEscapeTags(match) { - return `${START_ESCAPE_STR}${match}${END_ESCAPE_STR}`; -} - -function removeEscapeTags(_, p1) { - return p1; -} +const REGEX = new RegExp('{% *raw *%}(.*?){% *endraw *%}', 'gs'); function preEscapeRawTags(pageData) { - return pageData.replace(REGEX, addEscapeTags); + return pageData.replace(REGEX, `${START_ESCAPE_STR}$&${END_ESCAPE_STR}`); } module.exports = { @@ -19,13 +11,4 @@ module.exports = { const escapedPage = preEscapeRawTags(pageData); return nunjucks.renderString(escapedPage, variableMap, options); }, - - /** - * RemoveNunjucksEscapes removes raw tags from page data when processing - * Downstream calls to nunjuckUtils.renderEscaped will be essentially the same as nunjucks.renderString - * This ensures that we remove {{ and }} from the final output which prevents problem with Vue - */ - removeNunjucksEscapes(pageData) { - return pageData.replace(REGEX, removeEscapeTags); - }, }; diff --git a/test/functional/test_site/expected/index.html b/test/functional/test_site/expected/index.html index 0ad10605d7..71337fa7cc 100644 --- a/test/functional/test_site/expected/index.html +++ b/test/functional/test_site/expected/index.html @@ -490,11 +490,6 @@

    Markbind Plugin Pre-renderLevel 2 header (inside headingSearchIndex) with no-index attribute should not be indexed

    Level 6 header (outside headingSearchIndex) with always-index attribute should be indexed
    -

    Test NunjuckUtils

    -
    -

    The standard trick to render braces still works {{ content }}.

    -

    Nonexistent variables should still be removed: . In the rare event that user wrapped an unknown variable inside a raw tag, it should retain its current behaviour and not be produced to ensure Vue does not break.

    -

      diff --git a/test/functional/test_site/index.md b/test/functional/test_site/index.md index cebe9793b0..8821f50a97 100644 --- a/test/functional/test_site/index.md +++ b/test/functional/test_site/index.md @@ -285,7 +285,3 @@ tags: ["tag-frontmatter-shown", "tag-included-file", "+tag-exp*", "-tag-exp-hidd ## Level 2 header (inside headingSearchIndex) with no-index attribute should not be indexed {.no-index} ###### Level 6 header (outside headingSearchIndex) with always-index attribute should be indexed {.always-index} - -**Test NunjuckUtils** - - \ No newline at end of file diff --git a/test/functional/test_site/testNunjuckUtils.md b/test/functional/test_site/testNunjuckUtils.md deleted file mode 100644 index 164b365d2a..0000000000 --- a/test/functional/test_site/testNunjuckUtils.md +++ /dev/null @@ -1,7 +0,0 @@ -{%raw%} - -The standard trick to render braces still works {{ content }}. - -Nonexistent variables should still be removed: {{ unknown_variable }}. In the rare event that user wrapped an unknown variable inside a raw tag, it should retain its current behaviour and not be produced to ensure Vue does not break. - -{%endraw%} \ No newline at end of file diff --git a/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js b/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js index 65953d8828..2c219106df 100644 --- a/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js +++ b/test/functional/test_site_special_tags/_markbind/plugins/testSpecialTag.js @@ -2,10 +2,6 @@ const cheerio = module.parent.require('cheerio'); const ESCAPE_REGEX = new RegExp('{% *raw *%}(.*?){% *endraw *%}', 'gs'); -function removeEscapeTags(_, p1) { - return p1; -} - /* Simple test plugin that whitelists as a special tag. If encountered, it wraps the text node inside with some indication text as to @@ -23,20 +19,30 @@ function preRender(content) { $(testElement).text(wrappedText); }); + return $.html(); +} + +/* + Tests that special tags like which would contain a lot of mustache syntax + like {{ }}, we are able to replace them with !success!success success!success! + without interference from other dependencies +*/ +function postRender(content) { + const $ = cheerio.load(content); const escapedNunjucks = $('mustache'); escapedNunjucks.each((index, element) => { const unwrappedText = $(element).text(); - const unescapedText = unwrappedText.replace(ESCAPE_REGEX, removeEscapeTags); + const unescapedText = unwrappedText.replace(ESCAPE_REGEX, 'raw$1endraw'); const transformedText = unescapedText.replace(/{/g, '!success').replace(/}/g, 'success!'); $(element).text(transformedText); }); - return $.html(); } module.exports = { preRender, + postRender, getSpecialTags: () => ['testtag', 'mustache'], }; diff --git a/test/functional/test_site_special_tags/expected/index.html b/test/functional/test_site_special_tags/expected/index.html index a6c7719cf3..1085485b37 100644 --- a/test/functional/test_site_special_tags/expected/index.html +++ b/test/functional/test_site_special_tags/expected/index.html @@ -24,7 +24,7 @@

      Functional test for htmlparser2 and markdown-it patches for special tags

      So far as to comply with the commonmark spec

      -

      There should be no text between this and the next <hr> tag in the browser, since it is a <script> tag.
      There should be an alert with the value of 2 as well.

      +

      There should be no text between this and the next <hr> tag in the browser, since it is a <script> tag.
      There should be an alert with the value of 2 as well.