diff --git a/src/Site.js b/src/Site.js index a6b12cb9db..24342ed602 100644 --- a/src/Site.js +++ b/src/Site.js @@ -688,7 +688,6 @@ class Site { const filePathArray = Array.isArray(filePaths) ? filePaths : [filePaths]; const uniquePaths = _.uniq(filePathArray); logger.info('Rebuilding affected source files'); - this.variablePreprocessor.resetVariables(); return new Promise((resolve, reject) => { this.regenerateAffectedPages(uniquePaths) .then(() => fs.removeAsync(this.tempPath)) @@ -706,7 +705,6 @@ class Site { const uniqueUrls = _.uniq(normalizedUrlArray); uniqueUrls.forEach(normalizedUrl => logger.info( `Building ${normalizedUrl} as some of its dependencies were changed since the last visit`)); - this.variablePreprocessor.resetVariables(); /* Lazy loading only builds the page being viewed, but the user may be quick enough diff --git a/src/lib/markbind/src/constants.js b/src/lib/markbind/src/constants.js index 758777b634..3ff5b152fc 100644 --- a/src/lib/markbind/src/constants.js +++ b/src/lib/markbind/src/constants.js @@ -5,11 +5,6 @@ module.exports = { BOILERPLATE_FOLDER_NAME: '_markbind/boilerplates', - /* Imported global variables will be assigned a namespace. - * A prefix is appended to reduce clashes with other variables in the page. - */ - IMPORTED_VARIABLE_PREFIX: '$__MARKBIND__', - // src/lib/markbind/src/utils.js markdownFileExts: ['md', 'mbd', 'mbdf'], }; diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js index c18c7fcf2c..30a83b3303 100644 --- a/src/lib/markbind/src/parser.js +++ b/src/lib/markbind/src/parser.js @@ -235,21 +235,15 @@ class Parser { decodeEntities: true, }); - const outerContentRendered = this.variablePreprocessor.renderOuterVariables(content, file, - additionalVariables); - this.variablePreprocessor.extractInnerVariables(outerContentRendered, context.cwf) - .forEach(src => this.staticIncludeSrc.push(src)); - const innerContentRendered = this.variablePreprocessor.renderInnerVariables(outerContentRendered, - file, context.cwf, - additionalVariables); + const renderedContent = this.variablePreprocessor.renderPage(file, content, additionalVariables); const fileExt = utils.getExt(file); if (utils.isMarkdownFileExt(fileExt)) { context.source = 'md'; - parser.parseComplete(innerContentRendered.toString()); + parser.parseComplete(renderedContent.toString()); } else if (fileExt === 'html') { context.source = 'html'; - parser.parseComplete(innerContentRendered); + parser.parseComplete(renderedContent); } else { const error = new Error(`Unsupported File Extension: '${fileExt}'`); reject(error); diff --git a/src/lib/markbind/src/preprocessors/componentPreprocessor.js b/src/lib/markbind/src/preprocessors/componentPreprocessor.js index 4d22aee9f2..a4b7a749fc 100644 --- a/src/lib/markbind/src/preprocessors/componentPreprocessor.js +++ b/src/lib/markbind/src/preprocessors/componentPreprocessor.js @@ -244,9 +244,7 @@ function _preprocessInclude(node, context, config, parser) { const { renderedContent, childContext, - includedSrces, } = variablePreprocessor.renderIncludeFile(actualFilePath, element, context, filePath); - includedSrces.forEach(src => parser.staticIncludeSrc.push(src)); _deleteIncludeAttributes(element); @@ -317,6 +315,16 @@ function _preprocessVariables() { return utils.createEmptyNode(); } +function _preprocessImports(node, parser) { + if (node.attribs.from) { + parser.staticIncludeSrc.push({ + from: node.attribs.cwf, + to: path.resolve(node.attribs.cwf, node.attribs.from), + }); + } + + return utils.createEmptyNode(); +} /* * Body @@ -343,8 +351,9 @@ function preProcessComponent(node, context, config, parser) { case 'panel': return _preProcessPanel(element, context, config, parser); case 'variable': - case 'import': return _preprocessVariables(); + case 'import': + return _preprocessImports(node, parser); case 'include': return _preprocessInclude(element, context, config, parser); case 'body': diff --git a/src/lib/markbind/src/preprocessors/variablePreprocessor.js b/src/lib/markbind/src/preprocessors/variablePreprocessor.js index e2a83e52d5..4fa7a9c7c1 100644 --- a/src/lib/markbind/src/preprocessors/variablePreprocessor.js +++ b/src/lib/markbind/src/preprocessors/variablePreprocessor.js @@ -9,14 +9,12 @@ _.has = require('lodash/has'); _.isArray = require('lodash/isArray'); _.isEmpty = require('lodash/isEmpty'); -const md = require('../lib/markdown-it'); const njUtil = require('../utils/nunjuckUtils'); const urlUtils = require('../utils/urls'); const logger = require('../../../../util/logger'); const { ATTRIB_CWF, - IMPORTED_VARIABLE_PREFIX, } = require('../constants'); @@ -66,29 +64,6 @@ class VariablePreprocessor { * @type {Object>} */ this.userDefinedVariablesMap = {}; - - /* - * Page level - */ - - /** - * Map of file paths to another object containing its variable names matched to values - * @type {Object>} - */ - this.fileVariableMap = {}; - - /** - * Map of file paths to another object containing its - * variable aliases matched to the respective imported file - * @type {Object>} - */ - this.fileVariableAliasesMap = {}; - - /** - * Set of files that were already processed - * @type {Set} - */ - this.processedFiles = new Set(); } @@ -147,13 +122,21 @@ class VariablePreprocessor { /** - * Renders the supplied content using only the site variables belonging to the specified site of the file. + * Renders content belonging to a specified file path with the appropriate site's variables, + * with an optional set of lower and higher priority variables than the site variables to be rendered. * @param contentFilePath of the specified content to render - * @param content string + * @param content string to render + * @param lowerPriorityVariables than the site variables, if any + * @param higherPriorityVariables than the site variables, if any */ - renderSiteVariables(contentFilePath, content) { - const parentSite = urlUtils.getParentSiteAbsolutePath(contentFilePath, this.rootPath, this.baseUrlMap); - return njUtil.renderRaw(content, this.userDefinedVariablesMap[parentSite]); + renderSiteVariables(contentFilePath, content, lowerPriorityVariables = {}, higherPriorityVariables = {}) { + const userDefinedVariables = this.getContentParentSiteVariables(contentFilePath); + + return njUtil.renderRaw(content, { + ...lowerPriorityVariables, + ...userDefinedVariables, + ...higherPriorityVariables, + }); } @@ -162,220 +145,170 @@ class VariablePreprocessor { * Page level variable storage methods */ - - resetVariables() { - this.fileVariableMap = {}; - this.fileVariableAliasesMap = {}; - this.processedFiles.clear(); - } - /** - * Returns an object containing the ed variables for the specified file. - * For variables specified with aliases, we construct the sub object containing the - * names and values of the variables tied to the alias. - * @param file to get the imported variables for + * Subroutine for {@link extractPageVariables}. + * Renders a variable declared via either a tag or json file + * and then adds it to {@link pageVariables}. + * + * @param pageVariables object to add the extracted page variables to + * @param elem "dom node" as parsed by htmlparser2 + * @param elemHtml as outputted by $(elem).html() + * @param filePath that the tag is from + * @param renderVariable callback to render the extracted variable with before storing */ - getImportedVariableMap(file) { - const innerVariables = {}; - const fileAliases = this.fileVariableAliasesMap[file]; - - Object.entries(fileAliases).forEach(([alias, actualPath]) => { - innerVariables[alias] = {}; - const variables = this.fileVariableMap[actualPath]; + static addVariable(pageVariables, elem, elemHtml, filePath, renderVariable) { + const variableSource = elem.attribs.from; + if (variableSource) { + const variableFilePath = path.resolve(path.dirname(filePath), variableSource); + if (!fs.existsSync(variableFilePath)) { + logger.error(`The file ${variableSource} specified in 'from' attribute for json variable in ${ + filePath} does not exist!\n`); + return; + } + const rawData = fs.readFileSync(variableFilePath); - Object.entries(variables).forEach(([name, value]) => { - innerVariables[alias][name] = value; - }); - }); + try { + const jsonData = JSON.parse(rawData); + Object.entries(jsonData).forEach(([name, value]) => { + pageVariables[name] = renderVariable(filePath, value); + }); + } catch (err) { + logger.warn(`Error in parsing json from ${variableFilePath}:\n${err.message}\n`); + } + } else { + const variableName = elem.attribs.name; + if (!variableName) { + logger.warn(`Missing 'name' for variable in ${filePath}\n`); + return; + } - return innerVariables; + pageVariables[variableName] = renderVariable(filePath, elemHtml); + } } /** - * Retrieves a semi-unique alias for the given array of variable names from an import element - * by prepending the smallest variable name with $__MARKBIND__. - * Uses the alias declared in the 'as' attribute if there is one. - * @param variableNames of the import element, not referenced from the 'as' attribute - * @param node of the import element - * @return {string} Alias for the import element + * Subroutine for {@link extractPageVariables}. + * Processes an element with a 'from' attribute. + * {@link extractPageVariables} is recursively called to extract the other page's variables in doing so. + * Then, all locally declared variables of the imported file are assigned under the alias (if any), + * and any inline variables specified in the element are also set in {@link pageImportedVariables}. + * + * @param pageImportedVariables object to add the extracted imported variables to + * @param elem "dom node" of the element as parsed by htmlparser2 + * @param filePath that the tag is from + * @param renderVariable callback to render the 'from' attribute with */ - static getAliasForImportVariables(variableNames, node) { - if (_.has(node.attribs, 'as')) { - return node.attribs.as; + addImportVariables(pageImportedVariables, elem, filePath, renderVariable) { + // render the 'from' file path for the edge case that a variable is used inside it + const importedFilePath = renderVariable(filePath, elem.attribs.from); + const resolvedFilePath = path.resolve(path.dirname(filePath), importedFilePath); + if (!fs.existsSync(resolvedFilePath)) { + logger.error(`The file ${importedFilePath} specified in 'from' attribute for import in ${ + filePath} does not exist!\n`); + return; } - const largestName = variableNames.sort()[0]; - return IMPORTED_VARIABLE_PREFIX + largestName; - } - /** - * Extracts a pseudo imported variables for the supplied content. - * @param $ loaded cheerio object of the content - * @return {{}} pseudo imported variables of the content - */ - static extractPseudoImportedVariables($) { - const importedVariables = {}; - $('import[from]').each((index, element) => { - const variableNames = Object.keys(element.attribs).filter(name => name !== 'from' && name !== 'as'); - - // Add pseudo variables for the alias's variables - const alias = VariablePreprocessor.getAliasForImportVariables(variableNames, element); - importedVariables[alias] = new Proxy({}, { - get(obj, prop) { - return `{{${alias}.${prop}}}`; - }, - }); - - // Add pseudo variables for inline variables in the import element - variableNames.forEach((name) => { - importedVariables[name] = `{{${alias}.${name}}}`; - }); + // recursively extract the imported page's variables first + const importedFileContent = fs.readFileSync(resolvedFilePath); + const { pageVariables: importedFilePageVariables } = this.extractPageVariables(resolvedFilePath, + importedFileContent); + + const alias = elem.attribs.as; + if (alias) { + // import everything under the alias if there is one + pageImportedVariables[alias] = importedFilePageVariables; + } + + // additionally, import the inline variables without an alias + Object.keys(elem.attribs).filter((attr) => { + const isReservedAttribute = attr === 'from' || attr === 'as'; + if (isReservedAttribute) { + return false; + } + + const isExistingAttribute = !!importedFilePageVariables[attr]; + if (!isExistingAttribute) { + logger.warn(`Invalid inline attribute ${attr} imported in ${filePath} from ${resolvedFilePath}\n`); + return false; + } + + return true; + }).forEach((name) => { + pageImportedVariables[name] = importedFilePageVariables[name]; }); - return importedVariables; } /** * Extract page variables from a page. - * @param fileName for error printing + * These include all locally declared s and variables ed from other pages. + * @param filePath for error printing * @param data to extract variables from - * @param includedVariables variables from parent include + * @param includeVariables from the parent include, if any, used during {@link renderIncludeFile} */ - extractPageVariables(fileName, data, includedVariables) { - const fileDir = path.dirname(fileName); - const $ = cheerio.load(data); - - const importedVariables = VariablePreprocessor.extractPseudoImportedVariables($); + extractPageVariables(filePath, data, includeVariables = {}) { const pageVariables = {}; - const userDefinedVariables = this.getContentParentSiteVariables(fileName); + const pageImportedVariables = {}; - const setPageVariableIfNotSetBefore = (variableName, rawVariableValue) => { - if (pageVariables[variableName]) { - return; - } + const $ = cheerio.load(data); - pageVariables[variableName] = njUtil.renderRaw(rawVariableValue, { - ...importedVariables, + /* + This is used to render extracted variables before storing. + Hence, variables can be used within other variables, subject to declaration order. + */ + const renderVariable = (contentFilePath, content) => { + const previousVariables = { + ...pageImportedVariables, ...pageVariables, - ...userDefinedVariables, - ...includedVariables, - }, {}, false); + ...includeVariables, + }; + + return this.renderSiteVariables(contentFilePath, content, previousVariables); }; - $('variable').each((index, node) => { - const variableSource = $(node).attr('from'); - if (variableSource !== undefined) { - try { - const variableFilePath = path.resolve(fileDir, variableSource); - const jsonData = fs.readFileSync(variableFilePath); - const varData = JSON.parse(jsonData); - Object.entries(varData).forEach(([varName, varValue]) => { - setPageVariableIfNotSetBefore(varName, varValue); - }); - } catch (err) { - logger.warn(`Error ${err.message}`); - } + // NOTE: Selecting both at once is important to respect variable/import declaration order + $('variable, import[from]').not('include > import').each((index, elem) => { + if (elem.name === 'variable') { + VariablePreprocessor.addVariable(pageVariables, elem, $(elem).html(), filePath, renderVariable); } else { - const variableName = $(node).attr('name'); - if (!variableName) { - logger.warn(`Missing 'name' for variable in ${fileName}\n`); - return; - } - setPageVariableIfNotSetBefore(variableName, md.renderInline($(node).html())); + /* + NOTE: we pass renderVariable here as well but not for rendering ed variables again! + This is only for the edge case that variables are used in the 'from' attribute of + the which we must resolve first. + */ + this.addImportVariables(pageImportedVariables, elem, filePath, renderVariable); } }); - this.fileVariableMap[fileName] = pageVariables; - - return { ...importedVariables, ...pageVariables }; - } - - /** - * Extracts inner (nested) variables of the content, - * resolving the s with respect to the supplied contentFilePath. - * @param content to extract from - * @param contentFilePath of the content - * @return {[]} Array of files that were imported - */ - extractInnerVariables(content, contentFilePath) { - let staticIncludeSrcs = []; - const $ = cheerio.load(content, { - xmlMode: false, - decodeEntities: false, - }); - - const aliases = {}; - this.fileVariableAliasesMap[contentFilePath] = aliases; - - $('import[from]').each((index, node) => { - const filePath = path.resolve(path.dirname(contentFilePath), node.attribs.from); - staticIncludeSrcs.push({ from: contentFilePath, to: filePath }); - - const variableNames = Object.keys(node.attribs).filter(name => name !== 'from' && name !== 'as'); - const alias = VariablePreprocessor.getAliasForImportVariables(variableNames, node); - aliases[alias] = filePath; - - // Render inner file content and extract inner variables recursively - const { - content: renderedContent, - childContext, - } = this.renderIncludeFile(filePath, node, {}); - const userDefinedVariables = this.getContentParentSiteVariables(filePath); - staticIncludeSrcs = [ - ...staticIncludeSrcs, - ...this.extractInnerVariablesIfNotProcessed(renderedContent, childContext.cwf, filePath), - ]; - - // Re-render page variables with imported variables - const innerVariables = this.getImportedVariableMap(filePath); - const variables = this.fileVariableMap[filePath]; - Object.entries(variables).forEach(([variableName, value]) => { - variables[variableName] = njUtil.renderRaw(value, { - ...userDefinedVariables, ...innerVariables, - }); - }); - }); - - return staticIncludeSrcs; + return { pageImportedVariables, pageVariables }; } /** - * Extracts inner variables {@link extractInnerVariables} only if not processed before - */ - extractInnerVariablesIfNotProcessed(content, contentFilePath, filePathToExtract) { - if (!this.processedFiles.has(filePathToExtract)) { - this.processedFiles.add(filePathToExtract); - return this.extractInnerVariables(content, contentFilePath); - } - return []; - } - - /** - * Extracts variables specified as in include elements, - * adding them to the supplied variables if it does not already exist. + * Extracts variables specified as in include elements. * @param includeElement to extract inline variables from - * @param variables to add the extracted variables to */ - static extractIncludeInlineVariables(includeElement, variables) { + static extractIncludeInlineVariables(includeElement) { + const includeInlineVariables = {}; + Object.keys(includeElement.attribs).forEach((attribute) => { if (!attribute.startsWith('var-')) { return; } - const variableName = attribute.replace(/^var-/, ''); - if (!variables[variableName]) { - variables[variableName] = includeElement.attribs[attribute]; - } + const variableName = attribute.slice(4); + includeInlineVariables[variableName] = includeElement.attribs[attribute]; }); + + return includeInlineVariables; } /** - * Extracts variables specified as in include elements, - * adding them to the supplied variables if it does not already exist. + * Extracts variables specified as in include elements. * @param includeElement to search child nodes for - * @param variables to add the extracted variables to */ - static extractIncludeChildElementVariables(includeElement, variables) { + static extractIncludeChildElementVariables(includeElement) { if (!(includeElement.children && includeElement.children.length)) { - return; + return {}; } + const includeChildVariables = {}; includeElement.children.forEach((child) => { if (child.name !== 'variable' && child.name !== 'span') { @@ -383,15 +316,16 @@ class VariablePreprocessor { } const variableName = child.attribs.name || child.attribs.id; if (!variableName) { - // eslint-disable-next-line no-console - logger.warn(`Missing reference in ${includeElement.attribs[ATTRIB_CWF]}\n` - + `Missing 'name' or 'id' in variable for ${includeElement.attribs.src} include.`); + logger.warn(`Missing 'name' or 'id' in variable for ${includeElement.attribs.src}'s include in ${ + includeElement.attribs[ATTRIB_CWF]}.\n`); return; } - if (!variables[variableName]) { - variables[variableName] = cheerio.html(child.children); + if (!includeChildVariables[variableName]) { + includeChildVariables[variableName] = cheerio.html(child.children); } }); + + return includeChildVariables; } @@ -400,14 +334,15 @@ class VariablePreprocessor { * See https://markbind.org/userGuide/reusingContents.html#specifying-variables-in-an-include for usage. * It is a subroutine for {@link renderIncludeFile} * @param includeElement include element to extract variables from - * @param parentVariables defined by parent pages, which have greater priority */ - static extractIncludeVariables(includeElement, parentVariables) { - const includedVariables = { ...parentVariables }; - VariablePreprocessor.extractIncludeInlineVariables(includeElement, includedVariables); - VariablePreprocessor.extractIncludeChildElementVariables(includeElement, includedVariables); + static extractIncludeVariables(includeElement) { + const includeInlineVariables = VariablePreprocessor.extractIncludeInlineVariables(includeElement); + const includeChildVariables = VariablePreprocessor.extractIncludeChildElementVariables(includeElement); - return includedVariables; + return { + ...includeChildVariables, + ...includeInlineVariables, + }; } @@ -418,38 +353,40 @@ class VariablePreprocessor { /** - * Renders an include file with the supplied context, returning the rendered + * Renders an file with the supplied context, returning the rendered * content and new context with respect to the child content. * @param filePath of the included file source * @param node of the include element - * @param context object containing the variables for the current context + * @param context object containing the parent (if any) variables for the current context, + * which have greater priority than the extracted include variables of the current context. * @param asIfAt where the included file should be rendered from */ - renderIncludeFile(filePath, node, context, asIfAt = filePath) { + renderIncludeFile(filePath, node, context, asIfAt) { + const fileContent = fs.readFileSync(filePath, 'utf8'); + // Extract included variables from the include element, merging with the parent context variables - const includeVariables = VariablePreprocessor.extractIncludeVariables(node, context.variables); + const includeVariables = VariablePreprocessor.extractIncludeVariables(node); - // Extract page variables from the **included** file - const fileContent = fs.readFileSync(filePath, 'utf8'); - const userDefinedVariables = this.getContentParentSiteVariables(asIfAt); - const pageVariables = this.extractPageVariables(asIfAt, fileContent, includeVariables); + // We pass in includeVariables as well to render variables used in page s + // see "Test Page Variable and Included Variable Integrations" under test_site/index.md for an example + const { + pageImportedVariables, + pageVariables, + } = this.extractPageVariables(asIfAt, fileContent, includeVariables); // Render the included content with all the variables - const content = njUtil.renderRaw(fileContent, { + const renderedContent = this.renderSiteVariables(asIfAt, fileContent, { + ...pageImportedVariables, ...pageVariables, ...includeVariables, - ...userDefinedVariables, + ...context.variables, }); - const includedSrces = this.extractInnerVariablesIfNotProcessed(content, asIfAt, asIfAt); - const renderedContent = this.renderInnerVariables(content, asIfAt, asIfAt); - const childContext = _.cloneDeep(context); childContext.cwf = asIfAt; childContext.variables = includeVariables; return { - includedSrces, renderedContent, childContext, }; @@ -457,38 +394,27 @@ class VariablePreprocessor { /** - * Extracts the content page's variables, then renders it. - * @param content to render + * Renders content belonging to a page with the appropriate variables. + * In increasing order of priority (overriding), + * 1. ed variables as extracted during {@link extractPageVariables} + * 2. locally declared s as extracted during {@link extractPageVariables} + * (see https://markbind.org/userGuide/reusingContents.html#specifying-variables-in-an-include) + * 3. site variables as defined in variables.md * @param contentFilePath of the content to render - * @param additionalVariables if any - */ - renderOuterVariables(content, contentFilePath, additionalVariables = {}) { - const pageVariables = this.extractPageVariables(contentFilePath, content, {}); - const userDefinedVariables = this.getContentParentSiteVariables(contentFilePath); - - return njUtil.renderRaw(content, { - ...pageVariables, - ...userDefinedVariables, - ...additionalVariables, - }); - } - - /** - * Extracts the content's page's inner variables, then renders it. * @param content to render - * @param contentFilePath of the content to render - * @param contentAsIfAtFilePath - * @param additionalVariables if any + * @param highestPriorityVariables to render with the highest priority if any. + * This is currently only used for the MAIN_CONTENT_BODY in layouts. */ - renderInnerVariables(content, contentFilePath, contentAsIfAtFilePath, additionalVariables = {}) { - const userDefinedVariables = this.getContentParentSiteVariables(contentFilePath); - const innerVariables = this.getImportedVariableMap(contentAsIfAtFilePath); - - return njUtil.renderRaw(content, { - ...userDefinedVariables, - ...additionalVariables, - ...innerVariables, - }); + renderPage(contentFilePath, content, highestPriorityVariables = {}) { + const { + pageImportedVariables, + pageVariables, + } = this.extractPageVariables(contentFilePath, content); + + return this.renderSiteVariables(contentFilePath, content, { + ...pageImportedVariables, + ...pageVariables, + }, highestPriorityVariables); } } diff --git a/test/functional/test_site/expected/index.html b/test/functional/test_site/expected/index.html index 6002d95d8e..304056d7cb 100644 --- a/test/functional/test_site/expected/index.html +++ b/test/functional/test_site/expected/index.html @@ -161,7 +161,7 @@

Testing Site-NavHTML and Markdown +

Page Variable with HTML and Markdown

Nested Page Variable

@@ -194,6 +194,9 @@

Testing Site-NavHeading with multiple keywords

keyword 1 keyword 2

diff --git a/test/functional/test_site/expected/testImportVariables._include_.html b/test/functional/test_site/expected/testImportVariables._include_.html index dec4289734..005d1dabdf 100644 --- a/test/functional/test_site/expected/testImportVariables._include_.html +++ b/test/functional/test_site/expected/testImportVariables._include_.html @@ -6,7 +6,7 @@

Test import variables that itself imports other variables:

Trying to access a page variable:

There should be something red below:

-
This is a page variable from variablestoimport.md
+
This is a page variable from variablestoimport.md
Something should have appeared above in red.

Trying to access an imported variable via namespace:

There should be something blue below:

diff --git a/test/functional/test_site/expected/testImportVariables.html b/test/functional/test_site/expected/testImportVariables.html index c0e21d5e9c..e530aa08c9 100644 --- a/test/functional/test_site/expected/testImportVariables.html +++ b/test/functional/test_site/expected/testImportVariables.html @@ -34,7 +34,7 @@

Test import variables that itself imports other variables:

Trying to access a page variable:

There should be something red below:

-
This is a page variable from variablestoimport.md
+
This is a page variable from variablestoimport.md
Something should have appeared above in red.

Trying to access an imported variable via namespace:

There should be something blue below:

diff --git a/test/functional/test_site/index.md b/test/functional/test_site/index.md index 4e3f0e583f..7c1429ae04 100644 --- a/test/functional/test_site/index.md +++ b/test/functional/test_site/index.md @@ -40,6 +40,7 @@ tags: ["tag-frontmatter-shown", "tag-included-file", "+tag-exp*", "-tag-exp-hidd **Page Variable with HTML and MD** Page Variable with HTML and **Markdown** + {{ page_variable_with_HTML_and_MD }} **Nested Page Variable** @@ -75,6 +76,12 @@ tags: ["tag-frontmatter-shown", "tag-included-file", "+tag-exp*", "-tag-exp-hidd Included Variable Overriding Page Variable +Variables for includes should not be recognised as page variables, hence, there should be no text between **this** + +{{ included_variable_overriding_page_variable }} + +and **this**. + # Heading with multiple keywords keyword 1 keyword 2