Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/userGuide/syntax/variables.mbdf
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,26 @@ However, this is a combination of *both* syntaxes above, and thus this will allo

</box>

### Defining variables with JSON

You could also have your variables defined in a JSON file to define multiple variables in a more concise manner.

{{ icon_example }}
`variables.md`:
```html
<variable from="variables.json" />
```

`variables.json`:
```json
{
"variable1": "This is the first variable",
"variable2": "This is the second variable"
}
```

Variables defined in JSON file will be scoped according to where it is being referenced.

### Variables: Tips and Tricks

**Variables can refer to other variables** that are declared earlier, including built-in variables.
Expand Down Expand Up @@ -218,6 +238,25 @@ You must use the `safe` filter when using such variables:
<code>{<span></span>{ const_note }}</code> :fas-arrow-right: <span style="color: blue">Note: </span> This is a constant.
</div>

When defining variables with incomplete HTML fragments, we can define variables as a separate JSON file.

{{ icon_example }} variables containing HTML fragments:<br>

`index.md`:
```html
<variable from="variableFileName.json" />
```

`variableFileName.json`:
```json
{
"back_fragment": "Back</div>",
"front_fragment": "<div>Front"
}
```

<code>{<span></span>{ front_fragment }}</code> and <code>{<span></span>{ back_fragment }}</code> :fas-arrow-right: Front and Back

<span id="short" class="d-none">

Global variables:
Expand Down
28 changes: 23 additions & 5 deletions src/Site.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,10 @@ class Site {
this.baseUrlMap.forEach((base) => {
const userDefinedVariables = {};
Object.assign(userDefinedVariables, markbindVariable);

const userDefinedVariablesPath = path.resolve(base, USER_VARIABLES_PATH);
const userDefinedVariablesDir = path.dirname(userDefinedVariablesPath);
let content;
try {
const userDefinedVariablesPath = path.resolve(base, USER_VARIABLES_PATH);
content = fs.readFileSync(userDefinedVariablesPath, 'utf8');
} catch (e) {
content = '';
Expand All @@ -521,9 +521,27 @@ class Site {
const $ = cheerio.load(content);
$('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 = nunjuckUtils.renderEscaped(nunjucks, $(this).html(), userDefinedVariables);
userDefinedVariables[name] = html;
const variableSource = $(this).attr('from');

if (variableSource !== undefined) {
try {
const variableFilePath = path.resolve(userDefinedVariablesDir, variableSource);
const jsonData = fs.readFileSync(variableFilePath);
const varData = JSON.parse(jsonData);
Object.entries(varData).forEach(([varName, varValue]) => {
// Process the content of the variable with nunjucks, in case it refers to other variables.
const variableValue = nunjuckUtils.renderEscaped(nunjucks, varValue, userDefinedVariables);

userDefinedVariables[varName] = variableValue;
});
} catch (err) {
logger.warn(`Error ${err.message}`);
}
} else {
// Process the content of the variable with nunjucks, in case it refers to other variables.
const html = nunjuckUtils.renderEscaped(nunjucks, $(this).html(), userDefinedVariables);
userDefinedVariables[name] = html;
}
});
});
}
Expand Down
45 changes: 34 additions & 11 deletions src/lib/markbind/src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const slugify = require('@sindresorhus/slugify');
const componentParser = require('./parsers/componentParser');
const componentPreprocessor = require('./preprocessors/componentPreprocessor');
const nunjuckUtils = require('./utils/nunjuckUtils');
const logger = require('../../../util/logger');

const _ = {};
_.clone = require('lodash/clone');
Expand Down Expand Up @@ -67,6 +68,7 @@ class Parser {
*/
// eslint-disable-next-line class-methods-use-this
extractPageVariables(fileName, data, userDefinedVariables, includedVariables) {
const fileDir = path.dirname(fileName);
const $ = cheerio.load(data);
const pageVariables = {};
Parser.VARIABLE_LOOKUP.set(fileName, new Map());
Expand All @@ -92,21 +94,42 @@ class Parser {
importedVariables[name] = `{{${alias}.${name}}}`;
});
});
$('variable').each(function () {
const variableElement = $(this);
const variableName = variableElement.attr('name');
if (!variableName) {
// eslint-disable-next-line no-console
console.warn(`Missing 'name' for variable in ${fileName}\n`);
return;
}
const setPageVariable = (variableName, rawVariableValue) => {
const otherVariables = {
...importedVariables,
...pageVariables,
...userDefinedVariables,
...includedVariables,
};
const variableValue = nunjuckUtils.renderEscaped(nunjucks, rawVariableValue, otherVariables);
if (!pageVariables[variableName]) {
const variableValue = nunjuckUtils.renderEscaped(nunjucks, md.renderInline(variableElement.html()), {
...importedVariables, ...pageVariables, ...userDefinedVariables, ...includedVariables,
});
pageVariables[variableName] = variableValue;
Parser.VARIABLE_LOOKUP.get(fileName).set(variableName, variableValue);
}
};
$('variable').each(function () {
const variableElement = $(this);
const variableName = variableElement.attr('name');
const variableSource = $(this).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]) => {
setPageVariable(varName, varValue);
});
} catch (err) {
logger.warn(`Error ${err.message}`);
}
} else {
if (!variableName) {
// eslint-disable-next-line no-console
console.warn(`Missing 'name' for variable in ${fileName}\n`);
return;
}
setPageVariable(variableName, md.renderInline(variableElement.html()));
}
});
return { ...importedVariables, ...pageVariables };
}
Expand Down
3 changes: 3 additions & 0 deletions src/template/default/_markbind/variables.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"jsonVariableExample": "Your variables can be defined here as well"
}
4 changes: 3 additions & 1 deletion src/template/default/_markbind/variables.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<variable name="example">
To inject this HTML segment in your markbind files, use {{ example }} where you want to place it.
More generally, surround the segment's id with double curly braces.
</variable>
</variable>

<variable from="variables.json" />
3 changes: 3 additions & 0 deletions src/template/minimal/_markbind/variables.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"jsonVariableExample": "Your variables can be defined here as well"
}
4 changes: 3 additions & 1 deletion src/template/minimal/_markbind/variables.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<variable name="example">
To inject this HTML segment in your markbind files, use {{ example }} where you want to place it.
More generally, surround the segment's id with double curly braces.
</variable>
</variable>

<variable from="variables.json" />
6 changes: 6 additions & 0 deletions test/functional/test_site/_markbind/variable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"back": "back </div>",
"front": "<div> front",
"jsonVar1": "Json Variable can be referenced",
"jsonVar2": "Referencing jsonVar1: {{ jsonVar1 }}"
}
1 change: 1 addition & 0 deletions test/functional/test_site/_markbind/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
<variable name="global_variable_overriding_included_variable">Global Variable Overriding Included Variable</variable>
<variable name="global_variable">Global Variable</variable>
<variable name="page_global_variable_overriding_page_variable">Global Variable Overriding Page Variable</variable>
<variable from="variable.json"></variable>
6 changes: 6 additions & 0 deletions test/functional/test_site/expected/_markbind/variable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"front": "<div> front",
"back": "back </div>",
"jsonVar1": "Json Variable can be referenced",
"jsonVar2": "Referencing jsonVar1: {{ jsonVar1 }}"
}
6 changes: 5 additions & 1 deletion test/functional/test_site/expected/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,16 @@ <h3 id="testing-site-nav">Testing Site-Nav<a class="fa fa-anchor" href="#testing
<p>Here is a repeated footnote to <span for="pop:footnote1" v-b-popover.hover.top.html="popoverGenerator" v-b-tooltip.hover.top.html="tooltipContentGetter" v-on:mouseover="$refs['pop:footnote1'].show()" class="trigger"><sup class="footnote-ref"><a aria-describedby="footnote-label" href="#footnote1" id="footnoteref1:1">[1]</a></sup></span></p>
<p><strong>Inline footnotes:</strong> Here is an inline note.<span for="pop:footnote3" v-b-popover.hover.top.html="popoverGenerator" v-b-tooltip.hover.top.html="tooltipContentGetter" v-on:mouseover="$refs['pop:footnote3'].show()" class="trigger"><sup class="footnote-ref"><a aria-describedby="footnote-label" href="#footnote3" id="footnoteref3">[3]</a></sup></span></p>
</div>
<p><strong>Json Variable</strong></p>
<div> front back </div>
<p>Json Variable can be referenced Referencing jsonVar1: Json Variable can be referenced</p>
<p><strong>Variables that reference another variable</strong></p>
<p>This variable can be referenced.</p>
<p>References can be several levels deep.</p>
<p><strong>Page Variable</strong></p>
<div></div>
Page Variable
<div></div>
<p>Page Variable Json Variable</p>
<p><strong>Page Variable with HTML and MD</strong></p>
<div></div>
Page Variable with <span style="color: blue;">HTML</span> and <strong>Markdown</strong>
Expand Down
3 changes: 3 additions & 0 deletions test/functional/test_site/expected/jsonPageVariable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"json_page_variable": "Json Variable"
}
10 changes: 9 additions & 1 deletion test/functional/test_site/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ tags: ["tag-frontmatter-shown", "tag-included-file", "+tag-exp*", "-tag-exp-hidd

<include src="testFootnotes.md" />

**Json Variable**

{{ front }} {{ back }}

{{ jsonVar1 }} {{ jsonVar2 }}

**Variables that reference another variable**

{{finalized_value}}
Expand All @@ -27,7 +33,9 @@ tags: ["tag-frontmatter-shown", "tag-included-file", "+tag-exp*", "-tag-exp-hidd
**Page Variable**

<variable name="page_variable">Page Variable</variable>
{{ page_variable }}
<variable from="jsonPageVariable.json" />

{{ page_variable }} {{ json_page_variable }}

**Page Variable with HTML and MD**

Expand Down
3 changes: 3 additions & 0 deletions test/functional/test_site/jsonPageVariable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"json_page_variable": "Json Variable"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"jsonVariableExample": "Your variables can be defined here as well"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"jsonVariableExample": "Your variables can be defined here as well"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"jsonVariableExample": "Your variables can be defined here as well"
}
1 change: 1 addition & 0 deletions test/unit/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const MarkBind = require('../../src/lib/markbind/src/parser.js');
const { USER_VARIABLES_DEFAULT } = require('./utils/data');

jest.mock('fs');
jest.mock('../../src/util/logger');

afterEach(() => fs.vol.reset());

Expand Down