问题
I am trying to write a recursive Jekyll navigation template (include) as described in "Nested tree navigation with recursion". I have a minimal example committed in jekyll-min, which basically has:
- two top-level dirs, each with one page
- another dir under the second top-level dir, containing one page
- a navigation template (
_includes/docs_contents.html
) that loops through the top-level dirs and initiates recursive traversal for each - a recursive include (
_includes/nav.html
) that accepts a navigation entry, renders its title and child links, and invokes itself recursively for any dirs in its children list - a layout (
_layouts/doc.html
) that renders the navigation pane and content for each page
I'm using Ruby v2.7.0 and Jekyll v3.8.5.
# docs structure
_docs
|
|_a/
| |_index.md
|
|_b/
|_index.md
|
|_1/
|_index.md
# _data/docs-nav.yml
- title: a
docs:
- link: /a/
- title: b
docs:
- link: /b/
- title: 1
docs:
- link: /b/1/
# _includes/nav.html
{% assign section=include.nav %}
<div class="ui accordion">
<div class="title active">
<i class="dropdown icon"></i>
{{ section.title }}
</div>
<div class="content active">
<div class="ui vertical text menu">
{% for item in section.docs %}
{% if item.link %}
{%- assign p = site.documents | where: "url", item.link | first %}
<a {%- if page.url== p.url %} class="current item" {% endif %} class="item" href="{{ p.url }}">
{{ p.menu_name | default: p.title }}
</a>
{% endif %}
{% if item.docs %}
{% include nav.html nav=item %}
{% endif %}
{% endfor %}
</div>
</div>
</div>
# _includes/docs_contents.html
<div class="unit one-fifth hide-on-mobiles">
<aside>
{% for section in site.data.docs_nav %}
{% include nav.html nav=section %}
{% endfor %}
</aside>
</div>
# _layouts/doc.html
---
title: Docs
description: version 1.0
---
<html>
<body>
{% include docs_contents.html %}
{{ content }}
</body>
</html>
As far as I understand, for each page the navigation template render should work like this:
_layouts/doc.html
_includes/docs_contents.html
: iterate root level entries, calling_nav
for each_nav(/a/ entry)
: render title, iteratedocs
, render/a/
link, and quit_nav(/b/ entry)
: render title, iteratedocs
, render/b/
link, and then call_nav(/b/1/ entry)
_nav(/b/1/ entry)
: render title, iteratedocs
, render/b/1/
link, and quit_nav(/b/ entry)
(already in stack): quit_includes/docs_contents.html
: quit
However, when I perform a bundle exec jekyll build
I get:
Liquid Exception: Liquid error (/mnt/e/ThirdParty/jekyll-min/_includes/docs_contents.html line 17):
Nesting too deep included in /_layouts/doc.html
jekyll 3.8.5 | Error: Liquid error (/mnt/e/ThirdParty/jekyll-min/_includes/docs_contents.html line 17):
Nesting too deep included
Traceback (most recent call last):
[...]
What is the problem with my content or the recursive template? I have been struggling with this for hours with no luck.
JEKYLL_LOG_LEVEL=debug
didn't produce any additional useful info.
The actual document structure is more complex and could go arbitrarily deep, so writing a non-recursive template to manually handle nested levels may not be an option.
回答1:
Excellent question.
With help of {{ myvar | inspect }}
and a flag limiting recursion, I've successfully debugged your code and understood why this infinite recursion occurs.
It comes from the fact that the section
variable in docs_contents.html is assigned by in a for
loop and freezed : it cannot be changed.
The first time you include nav.html, {% assign section=include.nav %}
is not changing section
and your code just use the one assigned in your for
loop.
When you recurse and call nav.html a second time it will use the same freezed global section
variable and recurse indefinitely.
The solution is to change your variable name in nav.html from section
to something else. eg: sub_section
, and it will work, because this new variable will not be freezed and can be reassigned as needed during recursion.
{% assign sub_section=include.nav %}
{{ sub_section.title }}
{% for item in sub_section.docs %}
...
If you want to experiment here is my test code with some comments :
docs_contents.html
{% for section in site.data.docs_nav %}
{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from docs_contents.html" %}
{% assign recursion = 0 %}
<pre>
>> docs_contents.html
++++ "recursion" var is assigned and becomes global
recursion : {{ recursion | inspect }}
++++ "section" is freezed to loop value ++++
including nav with include nav.html nav=section >> {{ section | inspect }}
</pre>
{% include nav.html nav=section %}
{% endfor %}
nav.html
{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from nav.html" %}
<pre>
>> nav.hml
recursion : {{ recursion }}
include.nav : {{ include.nav | inspect }}
++++ "section" is freezed to loop value ++++
section : {{ section | inspect }}
</pre>
{% comment %} ++++ useless assignement ++++ {% endcomment %}
{% assign section=include.nav %}
{% for item in section.docs %}
{% if item.link %}
{%- assign p = site.documents | where: "url", item.link | first %}
<a {%- if page.url== p.url %} class="current item" {% endif %} class="item" href="{{ p.url }}">
{{ p.menu_name | default: p.title }}
</a>
{% endif %}
{% comment %}++++ limiting recursion to 2 levels ++++{% endcomment %}
{% if item.docs and recursion < 2 %}
{% comment %}++++ incrementing "recursion" global variable ++++{% endcomment %}
{% assign recursion = recursion | plus: 1 %}
{% include nav.html nav=item %}
{% endif %}
{% endfor %}
来源:https://stackoverflow.com/questions/60923504/how-to-fix-stacklevelerror-stack-overflow-in-jekyll-navigation-template