jdsteinbach / eleventy-plugin-toc

11ty plugin to generate a TOC from page content
62 stars 19 forks source link

`TemplateContentRenderError` was thrown > cheerio.load() expects a string #21

Open mariusa opened 3 years ago

mariusa commented 3 years ago

Hi, getting this error

`TemplateContentRenderError` was thrown
> cheerio.load() expects a string, file:./modules.md, line:9

`RenderError` was thrown
> cheerio.load() expects a string

`Error` was thrown:
    Error: cheerio.load() expects a string
        at Function.exports.load (/usr/local/lib/node_modules/eleventy-plugin-toc/node_modules/cheerio/lib/static.js:30:11)
        at BuildTOC (/usr/local/lib/node_modules/eleventy-plugin-toc/src/BuildTOC.js:18:21)
        at /usr/local/lib/node_modules/eleventy-plugin-toc/.eleventy.js:8:14

with a simple .md file

        <h2>{{ page.title }}</h2>
        <div class="line-shape"></div>

{{ content | toc : '{"tags":["h2","h3"],"wrapper":"div","wrapperClass":"content-tableau"}' }}
netmikey commented 3 years ago

I found out that this happens when you try to use content on a content template itself. I guess you have to use this outside the content, e.g. on the layout template.

mariusa commented 3 years ago

Hm, that means maintaining a separate almost duplicate layout only for the page(s) which need a TOC. I come from jekyll, where one can say in a markdown content page 'insert a TOC here', which makes sense. TOC could be after some page content, not necessarily at the beginning.

netmikey commented 3 years ago

You could use a custom flag on pages you want / don't want a TOC and read that flag in your layout? Something along those lines:

{% if page.includeToc %}
  <aside>
    {{ content | toc }}
  </aside>
{% endif %}

I myself would prefer having the TOC at a predefined position within the article (after the h1 and the abstract, before the first h2), pretty much like you explained. But that doesn't seem feasible from within the layout...?

(oh, and #22 is also a blocker for me)

jdsteinbach commented 3 years ago

Thanks for creating this issue.

First, the troubleshooting you all have done is correct: because this plugin provides a filter on content, it can only be used where Eleventy has the content variable available (like a layout template). Within a content file itself, content is undefined (it's not an object we can get a string from, as one comment suggested). Additionally, eleventyComputed doesn't have access to processed content in an .11tydata.js file.

Second, could you help me understand how using a layout template is a drawback here? As I approach this problem, that seems to be the right solution: since content doesn't exist until a content file has been processed, it's necessary to have a layout template if you need to use content.

@mariusa - do you have a link to Jekyll documentation/source for its TOC generation/insertion? I'll take a look at that to see if anything's comparable with Eleventy's processing pipeline.

mariusa commented 3 years ago

could you help me understand how using a layout template is a drawback here?

Example: a site with regular HTML pages, but also some .md pages (FAQ). Both use the same layout, which has header & footer.

FAQ page:

---
title: FAQ
---

Our APIs provide access to ...

<h4>Table of Contents</h4>
  * this unordered seed list will be replaced by toc as unordered list
  {:toc}

# Overview

## First topic here
...

This works with Jekyll. Good things:

  1. No need for an extra layout
  2. I can add custom page text/content before TOC for every page, all pages using the same layout

Jekyll how-to (no plugins required, using kramdown) https://stackoverflow.com/questions/38417624/table-of-contents-using-jekyll-and-kramdown