Skip to content

Commit

Permalink
Throw an error if a circular layout chain is detected (#2064)
Browse files Browse the repository at this point in the history
  • Loading branch information
AleksandrHovhannisyan committed Nov 6, 2021
1 parent 6c664cc commit bb4600e
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 1 deletion.
16 changes: 15 additions & 1 deletion src/TemplateLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,25 @@ class TemplateLayout extends TemplateContent {
let cfgKey = this.config.keys.layout;
let map = [];
let mapEntry = await this.getTemplateLayoutMapEntry();

// Keep track of every layout we see so we can detect cyclical layout chains (e.g., a => b => c => a).
const seenLayoutKeys = new Set();
seenLayoutKeys.add(mapEntry.key);
map.push(mapEntry);

while (mapEntry.frontMatterData && cfgKey in mapEntry.frontMatterData) {
// Layout of the current layout
const parentLayoutKey = mapEntry.frontMatterData[cfgKey];
// Abort if a circular layout chain is detected. Otherwise, we'll time out and run out of memory.
if (seenLayoutKeys.has(parentLayoutKey)) {
throw new Error(
`Circular layout chain detected starting at ${map[0].key}. The layout ${parentLayoutKey} was specified twice in this layout chain.`
);
}
// Keep track of this layout so we can detect duplicates in subsequent iterations
seenLayoutKeys.add(parentLayoutKey);
let layout = TemplateLayout.getTemplate(
mapEntry.frontMatterData[cfgKey],
parentLayoutKey,
mapEntry.inputDir,
this.config,
this.extensionMap
Expand Down
22 changes: 22 additions & 0 deletions test/TemplateLayoutTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ test("Get Layout Chain", async (t) => {
]);
});

test("Throw an error if a layout references itself as the layout", async (t) => {
await t.throwsAsync(async () => {
const tl = getTemplateLayoutInstance(
"layouts/layout-cycle-self.njk",
"./test/stubs"
);
const layoutChain = await tl._testGetLayoutChain();
return layoutChain;
});
});

test("Throw an error if a circular layout chain is detected", async (t) => {
await t.throwsAsync(async () => {
const tl = getTemplateLayoutInstance(
"layouts/layout-cycle-a.njk",
"./test/stubs"
);
const layoutChain = await tl._testGetLayoutChain();
return layoutChain;
});
});

test("Get Front Matter Data", async (t) => {
let tl = getTemplateLayoutInstance(
"layouts/layout-inherit-a.njk",
Expand Down
5 changes: 5 additions & 0 deletions test/stubs/_includes/layouts/layout-cycle-a.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
inherits: a
layout: layouts/layout-cycle-b.njk
---
{{content}} {{inherits}}
6 changes: 6 additions & 0 deletions test/stubs/_includes/layouts/layout-cycle-b.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
inherits: b
secondinherits: b
layout: layouts/layout-cycle-c.njk
---
{{ content | safe }} {{secondinherits}}
7 changes: 7 additions & 0 deletions test/stubs/_includes/layouts/layout-cycle-c.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
inherits: c
secondinherits: c
thirdinherits: c
layout: layouts/layout-cycle-a.njk
---
{{ content | safe }} {{inherits}} {{thirdinherits}}
3 changes: 3 additions & 0 deletions test/stubs/_includes/layouts/layout-cycle-self.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
layout: layouts/layout-cycle-self.njk
---

0 comments on commit bb4600e

Please sign in to comment.