Skip to content

Commit

Permalink
Merges code from #2076
Browse files Browse the repository at this point in the history
  • Loading branch information
zachleat committed May 9, 2022
1 parent 12fadd0 commit 13e7a52
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 1 deletion.
18 changes: 17 additions & 1 deletion src/TemplateLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,25 @@ class TemplateLayout extends TemplateContent {
let mapEntry = await this.getTemplateLayoutMapEntry();
map.push(mapEntry);

// 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);

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(
`Your layouts have a circular reference, 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
23 changes: 23 additions & 0 deletions test/TemplateLayoutTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,26 @@ test("Cache Duplicates (use full key for cache)", async (t) => {

t.is((await tla.render({})).trim(), "Hello A");
});

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

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(
"layout-cycle-a.njk",
"./test/stubsstubs-circular-layout"
);
const layoutChain = await tl._testGetLayoutChain();
return layoutChain;
});
});
3 changes: 3 additions & 0 deletions test/stubs-circular-layout/_includes/layout-cycle-a.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
layout: layouts/layout-cycle-b.njk
---
3 changes: 3 additions & 0 deletions test/stubs-circular-layout/_includes/layout-cycle-b.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
layout: layouts/layout-cycle-c.njk
---
3 changes: 3 additions & 0 deletions test/stubs-circular-layout/_includes/layout-cycle-c.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
layout: layouts/layout-cycle-a.njk
---
3 changes: 3 additions & 0 deletions test/stubs-circular-layout/_includes/layout-cycle-self.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
layout: layouts/layout-cycle-b.njk
---

0 comments on commit 13e7a52

Please sign in to comment.