Skip to content

Commit

Permalink
Allow specifying variables in include (#553)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamos-tay authored and yamgent committed Jan 28, 2019
1 parent bf07401 commit 1eae09f
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 1 deletion.
37 changes: 37 additions & 0 deletions docs/userGuide/reusingContents.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ As mentioned in _User Guide: MarkBind Syntax_, you can use built-in variables to
<p/>


##### Variables: Defaults

You can specify a default value for a variable, which is displayed when the variable is not specified in `variables.md` and by any of the [includes]({{ baseUrl }}/userGuide/reusingContents.html#the-include-tag) of the page. This is done by adding `or defaultValue` within the curly braces.

<div class="indented">

{{ icon_example }} If `name` is not declared in `variables.md`:<br>
<code>My name is {<span></span>{ name or "Anonymous" }}.</code> {{ icon_arrow_right }} My name is Anonymous.
</div>

##### Variables: Tips and Tricks

**Variables can refer to other variables** that are declared earlier, including built-in variables.
Expand Down Expand Up @@ -259,6 +269,33 @@ In other words, **`<include>` interprets the reused code relative to the origina

<hr><!-- ======================================================================================================= -->

##### Specifying Variables in an `<include>`

**It is possible to include variables in an `<include>`.**

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `<include>` tag:

```html
<include src="article.md">
<span id="title">My Title</span>
<span id="author">John Doe</span>
</include>
```

In `article.md`:

<code>
# {<span></span>{ title }}<br>
Author: {<span></span>{ author }}
</code>
</div>

These variables work the same way as variables in `_markbind/variables.md`, except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

If the same variable is defined in a chain of `<include>`s (e.g. `a.md` includes `b.md` includes `c.md`...), variables defined in the top-most `<include>` will take precedence. Global variables (`_markbind/variables.md`) will take precedence over any `<include>` variables.

## Using Boilerplate Files

**If you find duplicating a <tooltip content="code that needs to stay relative to the directory in which it used">_boilerplate code_</tooltip> fragment in multiple places of your code base, you can use a `boilerplate` file to avoid such duplication.** Note that you cannot use a normal `<include>` in this case because the code included using a normal `<include>` stays relative to the original location while boilerplate code needs to be interpreted relative to the location it is being used.
Expand Down
33 changes: 32 additions & 1 deletion src/lib/markbind/src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,32 @@ function Parser(options) {
this.missingIncludeSrc = [];
}

/**
* Extract variables from an include element
* @param includeElement include element to extract variables from
* @param contextVariables local variables defined by parent pages
*/
function extractVariables(includeElement, contextVariables) {
const includedVariables = { ...contextVariables };
if (includeElement.children) {
includeElement.children.forEach((child) => {
if (child.name !== 'span') {
return;
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${includeElement.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${includeElement.attribs.src} include.`);
return;
}
if (!includedVariables[child.attribs.id]) {
includedVariables[child.attribs.id] = cheerio.html(child.children);
}
});
}
return includedVariables;
}

Parser.prototype.getDynamicIncludeSrc = function () {
return _.clone(this.dynamicIncludeSrc);
};
Expand Down Expand Up @@ -205,7 +231,11 @@ Parser.prototype._preprocess = function (node, context, config) {
let fileContent = self._fileCache[actualFilePath]; // cache the file contents to save some I/O
const { parent, relative } = calculateNewBaseUrls(filePath, config.rootPath, config.baseUrlMap);
const userDefinedVariables = config.userDefinedVariablesMap[path.resolve(parent, relative)];
fileContent = nunjucks.renderString(fileContent, userDefinedVariables);

// process variables declared within the include
const includedVariables = extractVariables(element, context.includedVariables);

fileContent = nunjucks.renderString(fileContent, { ...includedVariables, ...userDefinedVariables });
delete element.attribs.boilerplate;
delete element.attribs.src;
delete element.attribs.inline;
Expand Down Expand Up @@ -265,6 +295,7 @@ Parser.prototype._preprocess = function (node, context, config) {
const childContext = _.cloneDeep(context);
childContext.cwf = filePath;
childContext.source = isIncludeSrcMd ? 'md' : 'html';
childContext.includedVariables = includedVariables;
if (element.children && element.children.length > 0) {
element.children = element.children.map(e => self._preprocess(e, childContext, config));
}
Expand Down
3 changes: 3 additions & 0 deletions test/test_site/_markbind/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
<span id="reference_level_2">{{reference_level_1}}</span>
<span id="reference_level_3">{{reference_level_2}}</span>
<span id="reference_level_4">{{reference_level_3}}</span>

<span id="global_variable_overriding_included_variable">Global variable overriding included variable</span>
<span id="included_global_variable">Global variable</span>
45 changes: 45 additions & 0 deletions test/test_site/expected/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@
<nav class="nav nav-pills flex-column my-0" style="margin-left: 5%; flex-wrap: nowrap;">
<a class="nav-link py-1" href="#fragment-with-leading-spaces-and-newline">Fragment with leading spaces and newline&#x200E;</a>
</nav>
<a class="nav-link py-1" href="#include-with-custom-variables">Include with custom variables&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variable">Test included variable&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variable-with-markdown">Test included variable with markdown&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variable-as-attribute">Test included variable as attribute&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variable-as-html-element">Test included variable as html element&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variable-overridden-by-variables-md">Test included variable overridden by variables.md&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variables-in-included-file">Test included variables in included file&#x200E;</a>
<a class="nav-link py-1" href="#inner-included-variables-should-not-leak-into-other-files">Inner included variables should not leak into other files&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variable-with-global-variable">Test included variable with global variable&#x200E;</a>
<a class="nav-link py-1" href="#test-included-variable-overridden-by-set">Test included variable overridden by set&#x200E;</a>
<a class="nav-link py-1" href="#test-missing-variable-with-default">Test missing variable with default&#x200E;</a>
<a class="nav-link py-1" href="#included-variables-should-not-leak-into-other-files">Included variables should not leak into other files&#x200E;</a>
<a class="nav-link py-1" href="#panel-without-src">Panel without src&#x200E;</a>
<nav class="nav nav-pills flex-column my-0" style="margin-left: 5%; flex-wrap: nowrap;">
<a class="nav-link py-1" href="#panel-without-src-header">Panel without src header&#x200E;</a>
Expand Down Expand Up @@ -365,6 +377,39 @@ <h2 id="feature-list">Feature list<a class="fa fa-anchor" href="#feature-list"><
<img src="/test_site/sub_site/images/I'm not allowed to use my favorite tool.png"></div>
<h1 id="trimmed-include">Trimmed include<a class="fa fa-anchor" href="#trimmed-include"></a></h1>
<h2 id="fragment-with-leading-spaces-and-newline"><span>Fragment with leading spaces and newline</span><a class="fa fa-anchor" href="#fragment-with-leading-spaces-and-newline"></a></h2>
<h1 id="include-with-custom-variables">Include with custom variables<a class="fa fa-anchor" href="#include-with-custom-variables"></a></h1>
<div>
<h1 id="test-included-variable">Test included variable<a class="fa fa-anchor" href="#test-included-variable"></a></h1>
<p>Included variable</p>
<h1 id="test-included-variable-with-markdown">Test included variable with markdown<a class="fa fa-anchor" href="#test-included-variable-with-markdown"></a></h1>
<p><strong><strong>Included variable with markdown</strong></strong>
</p>
<h1 id="test-included-variable-as-attribute">Test included variable as attribute<a class="fa fa-anchor" href="#test-included-variable-as-attribute"></a></h1>
<p style="color: blue">Test</p>
<h1 id="test-included-variable-as-html-element">Test included variable as html element<a class="fa fa-anchor" href="#test-included-variable-as-html-element"></a></h1>
<p><span>Included variable within html element</span></p>
<h1 id="test-included-variable-overridden-by-variables-md">Test included variable overridden by <a href="http://variables.md">variables.md</a><a class="fa fa-anchor" href="#test-included-variable-overridden-by-variables-md"></a></h1>
<p>Global variable overriding included variable</p>
<h1 id="test-included-variables-in-included-file">Test included variables in included file<a class="fa fa-anchor" href="#test-included-variables-in-included-file"></a></h1>
<div>
<p>Included variable in outer included file</p>
<p>Included variable overriding inner variable</p>
</div>
<h1 id="inner-included-variables-should-not-leak-into-other-files">Inner included variables should not leak into other files<a class="fa fa-anchor" href="#inner-included-variables-should-not-leak-into-other-files"></a></h1>
<div>
<p>Should be blank:</p>
</div>
<h1 id="test-included-variable-with-global-variable">Test included variable with global variable<a class="fa fa-anchor" href="#test-included-variable-with-global-variable"></a></h1>
<p>Included variable with Global variable</p>
<h1 id="test-included-variable-overridden-by-set">Test included variable overridden by set<a class="fa fa-anchor" href="#test-included-variable-overridden-by-set"></a></h1>
<p>Inner variable overridden by set Global variable overridden by set</p>
<h1 id="test-missing-variable-with-default">Test missing variable with default<a class="fa fa-anchor" href="#test-missing-variable-with-default"></a></h1>
<p>Missing Variable</p>
</div>
<h1 id="included-variables-should-not-leak-into-other-files">Included variables should not leak into other files<a class="fa fa-anchor" href="#included-variables-should-not-leak-into-other-files"></a></h1>
<div>
<p>Should be blank:</p>
</div>
<h1 id="panel-without-src">Panel without src<a class="fa fa-anchor" href="#panel-without-src"></a></h1>
<panel header="## Panel without src header<a class='fa fa-anchor' href='#panel-without-src-header'></a>" expanded="">
<div>
Expand Down
12 changes: 12 additions & 0 deletions test/test_site/expected/siteData.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@
"include-from-another-markbind-site": "Include from another Markbind site",
"trimmed-include": "Trimmed include",
"fragment-with-leading-spaces-and-newline": "Fragment with leading spaces and newline",
"include-with-custom-variables": "Include with custom variables",
"test-included-variable": "Test included variable",
"test-included-variable-with-markdown": "Test included variable with markdown",
"test-included-variable-as-attribute": "Test included variable as attribute",
"test-included-variable-as-html-element": "Test included variable as html element",
"test-included-variable-overridden-by-variables-md": "Test included variable overridden by variables.md",
"test-included-variables-in-included-file": "Test included variables in included file",
"inner-included-variables-should-not-leak-into-other-files": "Inner included variables should not leak into other files",
"test-included-variable-with-global-variable": "Test included variable with global variable",
"test-included-variable-overridden-by-set": "Test included variable overridden by set",
"test-missing-variable-with-default": "Test missing variable with default",
"included-variables-should-not-leak-into-other-files": "Included variables should not leak into other files",
"panel-without-src": "Panel without src",
"panel-with-normal-src": "Panel with normal src",
"panel-with-src-from-a-page-segment": "Panel with src from a page segment",
Expand Down
18 changes: 18 additions & 0 deletions test/test_site/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ head: myCustomHead.md, myCustomHead2.md

## <include src="testTrimInclude.md" trim inline />

# Include with custom variables

<include src="testIncludeVariables.md">
<span id="included_variable">Included variable</span>
<span id="included_variable_with_markdown">__**Included variable with markdown**__</span>
<span id="included_variable_as_attribute">color: blue</span>
<span id="included_variable_as_html_element"><span>Included variable within html element</span></span>
<span id="global_variable_overriding_included_variable">Included variable overridden by global variable</span>
<span id="included_variable_inner_overridden">Included variable overriding inner variable</span>
<span id="included_variable_in_outer_included_file">Included variable in outer included file</span>
<span id="included_variable_should_not_leak">Included variable should not leak into other files</span>
<span id="included_variable_with_global_variable">Included variable with {{ included_global_variable }}</span>
</include>

# Included variables should not leak into other files

<include src="testIncludeVariableLeak.md" />

# Panel without src
<panel header="## Panel without src header" expanded>
<markdown>
Expand Down
1 change: 1 addition & 0 deletions test/test_site/testIncludeVariableLeak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Should be blank: {{ included_variable_should_not_leak }}
1 change: 1 addition & 0 deletions test/test_site/testIncludeVariableLeakInner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Should be blank: {{ included_variable_should_not_leak_inner }}
36 changes: 36 additions & 0 deletions test/test_site/testIncludeVariables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Test included variable
{{ included_variable }}

# Test included variable with markdown
{{ included_variable_with_markdown }}

# Test included variable as attribute
<p style="{{ included_variable_as_attribute }}">Test</p>

# Test included variable as html element
{{ included_variable_as_html_element }}

# Test included variable overridden by variables.md
{{ global_variable_overriding_included_variable }}

# Test included variables in included file
<include src="testIncludeVariablesIncludedFile.md">
<span id="included_variable_inner_overridden">Included variable overridden by outer variable</span>
<span id="included_variable_should_not_leak_inner">Included variable should not leak into other files</span>
</include>

# Inner included variables should not leak into other files
<include src="testIncludeVariableLeakInner.md" />

# Test included variable with global variable
{{ included_variable_with_global_variable }}

{% set included_variable = "Inner variable overridden by set" %}
{% set included_global_variable = "Global variable overridden by set" %}

# Test included variable overridden by set
{{ included_variable }}
{{ included_global_variable }}

# Test missing variable with default
{{ missing_variable or "Missing Variable" }}
3 changes: 3 additions & 0 deletions test/test_site/testIncludeVariablesIncludedFile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{ included_variable_in_outer_included_file }}

{{ included_variable_inner_overridden }}

0 comments on commit 1eae09f

Please sign in to comment.