diff --git a/package-lock.json b/package-lock.json
index 257fe4e746..658b6dcf46 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2255,11 +2255,6 @@
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
- "ejs": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz",
- "integrity": "sha512-cuIMtJwxvzumSAkqaaoGY/L6Fc/t6YvoP9/VIaK0V/CyqKLEQ8sqODmYfy/cjXEdZ9+OOL8TecbJu+1RsofGDw=="
- },
"electron-to-chromium": {
"version": "1.3.322",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz",
diff --git a/package.json b/package.json
index f5e7b2adab..1a7855ff34 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,6 @@
"cheerio": "^0.22.0",
"chokidar": "^3.3.0",
"commander": "^3.0.2",
- "ejs": "^3.0.1",
"fastmatter": "^2.1.1",
"figlet": "^1.2.4",
"find-up": "^4.1.0",
diff --git a/src/Page.js b/src/Page.js
index 8ee0af33f3..31a3a704c0 100644
--- a/src/Page.js
+++ b/src/Page.js
@@ -827,7 +827,7 @@ Page.prototype.generate = function (builtFiles) {
this.buildPageNav();
return fs.outputFileAsync(this.resultPath, htmlBeautify(
- this.template(this.prepareTemplateData()),
+ this.template.render(this.prepareTemplateData()),
{ indent_size: 2 },
));
})
diff --git a/src/Site.js b/src/Site.js
index 4e2c22e718..2334f9c7c0 100644
--- a/src/Site.js
+++ b/src/Site.js
@@ -1,5 +1,4 @@
const cheerio = require('cheerio');
-const ejs = require('ejs');
const fs = require('fs-extra-promise');
const ghpages = require('gh-pages');
const ignore = require('ignore');
@@ -112,7 +111,8 @@ function Site(rootPath, outputPath, onePagePath, forceReload = false, siteConfig
// Page template path
this.pageTemplatePath = path.join(__dirname, PAGE_TEMPLATE_NAME);
- this.pageTemplate = ejs.compile(fs.readFileSync(this.pageTemplatePath, 'utf8'));
+ const env = nunjucks.configure({ autoescape: false });
+ this.pageTemplate = nunjucks.compile(fs.readFileSync(this.pageTemplatePath, 'utf8'), env);
this.pages = [];
// Other properties
diff --git a/src/constants.js b/src/constants.js
index 58872e2e2e..903e603ff0 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -46,7 +46,7 @@ module.exports = {
FOOTER_PATH: '_markbind/footers/footer.md',
INDEX_MARKDOWN_FILE: 'index.md',
MARKBIND_PLUGIN_PREFIX: 'markbind-plugin-',
- PAGE_TEMPLATE_NAME: 'page.ejs',
+ PAGE_TEMPLATE_NAME: 'page.njk',
PROJECT_PLUGIN_FOLDER_NAME: '_markbind/plugins',
SITE_CONFIG_NAME: 'site.json',
SITE_DATA_NAME: 'siteData.json',
diff --git a/src/page.ejs b/src/page.ejs
deleted file mode 100644
index d85e19e29b..0000000000
--- a/src/page.ejs
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
- <%- headFileTopContent %>
-
-
-
-
- <%= title %>
-
-
-
-
-
-
-<% if (asset.pluginLinks) { -%>
-<% for (const link of asset.pluginLinks) { -%>
- <%- link %>
-<% } -%>
-<% } -%>
-
- <% if (siteNav) { %><% } %>
- <% if (pageNav) { %><% } %>
- <%- headFileBottomContent %>
- <% if (faviconUrl) { %><% } %>
-
- data-spy="scroll" data-target="#page-nav" data-offset="100" <%_ } %>>
-
- <%- headerHtml _%>
-
- <%- siteNavHtml _%>
- <%- content %>
- <%- pageNavHtml _%>
-
- <%- footerHtml _%>
-
-
-
-
-
-
-
-
-
-<% if (asset.externalScripts) { -%>
-<% for (const script of asset.externalScripts) { -%>
-
-<% } -%>
-<% } -%>
-<% if (asset.pluginScripts) { -%>
-<% for (const script of asset.pluginScripts) { -%>
-<%- script %>
-<% } -%>
-<% } -%>
-
-
diff --git a/src/page.njk b/src/page.njk
new file mode 100644
index 0000000000..c0a8860e6e
--- /dev/null
+++ b/src/page.njk
@@ -0,0 +1,68 @@
+
+
+
+ {{ headFileTopContent }}
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+ {%- if asset.pluginLinks %}
+ {%- for link in asset.pluginLinks %}
+ {{ link }}
+ {%- endfor %}
+ {%- endif %}
+
+ {%- if siteNav %}
+
+ {%- endif %}
+ {%- if pageNav %}
+
+ {%- endif %}
+ {{ headFileBottomContent }}
+ {%- if faviconUrl %}
+
+ {%- endif %}
+
+
+
+ {{ headerHtml }}
+
+ {{ siteNavHtml }}
+ {{ content }}
+ {{ pageNavHtml }}
+
+ {{ footerHtml }}
+
+
+
+
+
+
+
+
+
+
+{%- if asset.externalScripts %}
+{%- for script in asset.externalScripts %}
+
+{%- endfor %}
+{%- endif %}
+
+{%- if asset.pluginScripts %}
+{%- for script in asset.pluginScripts %}
+ {{ script }}
+{%- endfor %}
+{%- endif %}
+
+
+
diff --git a/test/unit/Site.test.js b/test/unit/Site.test.js
index 4dbf6f8c85..43b6f390f0 100644
--- a/test/unit/Site.test.js
+++ b/test/unit/Site.test.js
@@ -8,7 +8,7 @@ const {
FOOTER_MD_DEFAULT,
HEADER_MD_DEFAULT,
INDEX_MD_DEFAULT,
- PAGE_EJS,
+ PAGE_NJK,
SITE_JSON_DEFAULT,
SITE_NAV_MD_DEFAULT,
TOP_NAV_DEFAULT,
@@ -29,7 +29,7 @@ afterEach(() => fs.vol.reset());
test('Site Generate builds the correct amount of assets', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'inner/site.json': SITE_JSON_DEFAULT,
'asset/css/bootstrap.min.css': '',
@@ -102,7 +102,7 @@ test('Site Generate builds the correct amount of assets', async () => {
test('Site Init with invalid template fails', async () => {
// Mock default template in MemFS without site config
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'src/template/default/index.md': INDEX_MD_DEFAULT,
};
@@ -121,7 +121,7 @@ test('Site Init does not overwrite existing files', async () => {
// Mock default template in MemFS
const json = {
'index.md': EXISTING_INDEX_MD,
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'src/template/default/index.md': INDEX_MD_DEFAULT,
'src/template/default/site.json': SITE_JSON_DEFAULT,
};
@@ -135,7 +135,7 @@ test('Site Init does not overwrite existing files', async () => {
test('Site Init in existing directory generates correct assets', async () => {
// Mock default template in MemFS
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'src/template/default/index.md': INDEX_MD_DEFAULT,
'src/template/default/site.json': SITE_JSON_DEFAULT,
'src/template/default/_markbind/boilerplates/': '',
@@ -199,7 +199,7 @@ test('Site Init in existing directory generates correct assets', async () => {
test('Site Init in directory which does not exist generates correct assets', async () => {
// Mock default template in MemFS
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'src/template/default/index.md': INDEX_MD_DEFAULT,
'src/template/default/site.json': SITE_JSON_DEFAULT,
'src/template/default/_markbind/boilerplates/': '',
@@ -265,7 +265,7 @@ test('Site Init in directory which does not exist generates correct assets', asy
test('Site baseurls are correct for sub nested subsites', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
'sub/site.json': SITE_JSON_DEFAULT,
'sub/sub/site.json': SITE_JSON_DEFAULT,
@@ -282,7 +282,7 @@ test('Site baseurls are correct for sub nested subsites', async () => {
test('Site removeAsync removes the correct asset', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'_site/toRemove.jpg': '',
'_site/dontRemove.png': '',
'toRemove.html': '',
@@ -297,7 +297,7 @@ test('Site removeAsync removes the correct asset', async () => {
test('Site read site config for default', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
};
fs.vol.fromJSON(json, '');
@@ -329,7 +329,7 @@ test('Site read site config for custom site config', async () => {
enableSearch: true,
};
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': JSON.stringify(customSiteJson),
};
fs.vol.fromJSON(json, '');
@@ -341,7 +341,7 @@ test('Site read site config for custom site config', async () => {
test('Site resolves variables referencing other variables', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
'_markbind/variables.md':
'variable'
@@ -361,17 +361,13 @@ test('Site resolves variables referencing other variables', async () => {
expect(root.level1).toEqual('variable');
expect(root.level2).toEqual('variable');
const expectedTextSpan = 'Blue text';
- const expectedTextSpanEscaped = expectedTextSpan
- .replace(/"/g, '"')
- .replace(//g, '>');
expect(root.level3).toEqual(expectedTextSpan);
- expect(root.level4).toEqual(expectedTextSpanEscaped);
+ expect(root.level4).toEqual(expectedTextSpan);
});
test('Site read correct user defined variables', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
'sub/site.json': SITE_JSON_DEFAULT,
'sub/sub/site.json': SITE_JSON_DEFAULT,
@@ -414,7 +410,7 @@ test('Site read correct user defined variables', async () => {
test('Site convert generates correct assets', async () => {
// Mock default template in MemFS
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'src/template/default/index.md': INDEX_MD_DEFAULT,
'src/template/default/site.json': SITE_JSON_DEFAULT,
'src/template/default/_markbind/boilerplates/': '',
@@ -467,7 +463,7 @@ test('Site convert generates correct assets', async () => {
test('Site convert with custom _Footer.md, no _Sidebar.md, README.md generates correct assets', async () => {
// Mock default template in MemFS
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'src/template/default/index.md': INDEX_MD_DEFAULT,
'src/template/default/site.json': SITE_JSON_DEFAULT,
'src/template/default/_markbind/boilerplates/': '',
@@ -523,7 +519,7 @@ test('Site convert with custom _Footer.md, no _Sidebar.md, README.md generates c
test('Site deploys with default settings', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
_site: {},
};
@@ -543,7 +539,7 @@ test('Site deploys with custom settings', async () => {
branch: 'master',
};
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': JSON.stringify(customConfig),
_site: {},
};
@@ -557,7 +553,7 @@ test('Site deploys with custom settings', async () => {
test('Site should not deploy without a built site', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
};
fs.vol.fromJSON(json, '');
@@ -591,7 +587,7 @@ describe('Site deploy with Travis', () => {
process.env.TRAVIS_REPO_SLUG = 'TRAVIS_USER/TRAVIS_REPO';
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
_site: {},
};
@@ -611,7 +607,7 @@ describe('Site deploy with Travis', () => {
const customRepoConfig = JSON.parse(SITE_JSON_DEFAULT);
customRepoConfig.deploy.repo = 'https://github.com/USER/REPO.git';
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': JSON.stringify(customRepoConfig),
_site: {},
};
@@ -628,7 +624,7 @@ describe('Site deploy with Travis', () => {
process.env.TRAVIS_REPO_SLUG = 'TRAVIS_USER/TRAVIS_REPO.github.io';
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
_site: {},
};
@@ -641,7 +637,7 @@ describe('Site deploy with Travis', () => {
test('Site deploy -t/--travis should not deploy if not in Travis', async () => {
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
_site: {},
};
@@ -656,7 +652,7 @@ describe('Site deploy with Travis', () => {
process.env.TRAVIS = true;
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': SITE_JSON_DEFAULT,
_site: {},
};
@@ -674,7 +670,7 @@ describe('Site deploy with Travis', () => {
const invalidRepoConfig = JSON.parse(SITE_JSON_DEFAULT);
invalidRepoConfig.deploy.repo = 'INVALID_GITHUB_REPO';
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'site.json': JSON.stringify(invalidRepoConfig),
_site: {},
};
@@ -792,7 +788,7 @@ siteJsonResolvePropertiesTestCases.forEach((testCase) => {
},
};
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'index.md': '',
};
fs.vol.fromJSON(json, '');
@@ -828,7 +824,7 @@ test('Site config throws error on duplicate page src', async () => {
},
};
const json = {
- 'src/page.ejs': PAGE_EJS,
+ 'src/page.njk': PAGE_NJK,
'index.md': '',
};
fs.vol.fromJSON(json, '');
diff --git a/test/unit/utils/data.js b/test/unit/utils/data.js
index 8fa0a88abb..60852febb2 100644
--- a/test/unit/utils/data.js
+++ b/test/unit/utils/data.js
@@ -7,44 +7,52 @@ module.exports.LAYOUT_FILES_DEFAULT = [
];
module.exports.LAYOUT_SCRIPTS_DEFAULT = '// eslint-disable-next-line no-undef\n'
-+ 'MarkBind.afterSetup(() => {\n'
-+ ' // Include code to be called after MarkBind setup here.\n'
-+ '});\n';
+ + 'MarkBind.afterSetup(() => {\n'
+ + ' // Include code to be called after MarkBind setup here.\n'
+ + '});\n';
-module.exports.PAGE_EJS = '\n'
- + '\n'
- + '\n'
- + ' <%- headFileTopContent %>\n'
- + ' \n'
- + ' \n'
- + ' \n'
- + ' <%= title %>\n'
- + ' \n'
- + ' \n'
- + ' \n'
- + ' \n'
- + ' \n'
- + ' \n'
- + ' \n'
- + ' <% if (siteNav) { %><% } %>\n'
- + ' <%- headFileBottomContent %>\n'
- + ' <% if (faviconUrl) { %><% } %>\n'
- + '\n'
- + '\n'
- + '\n'
- + ' <%- content %>\n'
- + '
\n'
- + '\n'
- + '\n'
- + '\n'
- + '\n'
- + '\n'
- + '\n'
- + '\n'
- + '\n'
- + '\n';
+module.exports.PAGE_NJK = `
+
+
+
+ {{ headFileTopContent }}
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+ {% if siteNav %}
+
+ {% endif %}
+ {{ headFileBottomContent }}
+ {% if faviconUrl %}
+
+ {% endif %}
+
+
+
+ {{ content }}
+
+
+
+
+
+
+
+
+
+
+
+`;
module.exports.SITE_JSON_DEFAULT = '{\n'
+ ' "baseUrl": "",\n'