Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shortcode: Post excerpt/summary – How to access post content? #179

Closed
kleinfreund opened this issue Jul 21, 2018 · 11 comments
Closed

Shortcode: Post excerpt/summary – How to access post content? #179

kleinfreund opened this issue Jul 21, 2018 · 11 comments
Assignees
Milestone

Comments

@kleinfreund
Copy link
Contributor

kleinfreund commented Jul 21, 2018

I’m currently writing a shortcode for post excerpts/summaries:

---
layout: layouts/base.liquid
pagination:
  data: collections.post
  size: 5
  alias: posts
---
{%- for post in posts -%}
<article>
  {% excerpt post %}
</article>
{%- endfor -%}

Something along the lines of this:

module.exports = function (eleventyConfig) {
  eleventyConfig.addLiquidShortcode('excerpt', post => {
    if (!post.templateContent) {
      return;
    }

    const content = post.templateContent;
    const excerptEnd = content.includes('<!--more-->')
      ? content.indexOf('<!--more-->')
      : content.indexOf('\n\n');

    return content.substring(0, excerptEnd).trim();
  });

  // …
};

Now, it seems like post.templateContent doesn’t always contain what I want. Sometimes, this property doesn’t exist. How would I go about this? Do I use post.inputContent and strip any front matter myself? Is that safe? Seems odd.

@zachleat
Copy link
Member

Hey @kleinfreund, where is posts coming from there?

@zachleat zachleat added the open-question Requires additional information before we can proceed. label Jul 21, 2018
@zachleat zachleat self-assigned this Jul 21, 2018
@kleinfreund
Copy link
Contributor Author

kleinfreund commented Jul 21, 2018

Ah, yeah, that should’ve been in the original post (added the front matter there now!). In the site I’m converting, I have a bunch of posts that are already tagged (e.g. tags: dev, etc.). Therefor, for testing, I have set pagination.data: collections.dev in order to test my blog listing plus pagination. That’s related to the tag merging issue (see #147).

When logging post in the shortcode JavaScript, I see a whole bunch of data, including post.templateContent, but that’s not always there. I haven’t figured out yet, why some posts have that I why some don’t. Probably looking into this tomorrow again.

@zachleat
Copy link
Member

Ah pagination, yeah. I’m almost certain this is related to #171 but I’ll leave it open until after 0.5.1 is out to confirm.

@zachleat
Copy link
Member

Well, I’m confident, maybe almost certain is a little too much 😅

@zachleat zachleat removed the open-question Requires additional information before we can proceed. label Jul 21, 2018
@zachleat zachleat added this to the Next Minor Version milestone Jul 21, 2018
@zachleat
Copy link
Member

zachleat commented Jul 21, 2018

The other thing this made me think of is the excerpt feature on gray-matter, which Eleventy has no support for (but certainly would be eligible for a good feature request).

https://www.npmjs.com/package/gray-matter#optionsexcerpt

---
myfrontmatteroption: true
---
this is an excerpt
---
this is my content

@zachleat zachleat added the bug label Jul 21, 2018
zachleat added a commit that referenced this issue Jul 22, 2018
@zachleat
Copy link
Member

zachleat commented Jul 22, 2018

I added some additional tests to confirm that this should work in 0.5.1

zachleat added a commit that referenced this issue Jul 24, 2018
@zachleat
Copy link
Member

I think I spoke too soon. I ended up making a huge refactoring change in the perf branch that will fix this. It’s unlikely to land in 0.5.1 but will for sure land in 0.6.0 after I’m comfortable it won’t break everything. Sorry!

@zachleat zachleat modified the milestones: Next Minor Version, 0.6.0 Jul 24, 2018
@zachleat
Copy link
Member

Work for this also created most of #108, so we got that going for us 😅

zachleat added a commit that referenced this issue Jul 24, 2018
@kleinfreund
Copy link
Contributor Author

No need to apologize. I takes what it takes. It seems very worthwhile.

zachleat added a commit that referenced this issue Jul 24, 2018
@zachleat zachleat modified the milestones: 0.6.0, 0.5.1 Jul 24, 2018
@zachleat
Copy link
Member

v0.5.1 is out, please retest! (and reopen if necessary)

@kleinfreund
Copy link
Contributor Author

kleinfreund commented Aug 15, 2018

Yes and no. I’m not quite sure. It works as in every post (i.e. every document in a collection) generates an excerpt, but for the first page in the pagination (with pagination size 5, that’s the first 5 documents), this is still triggered:

if (!doc.hasOwnProperty('templateContent')) {
  console.warn('>>> Failed to extract excerpt: Document has no property `templateContent`.');
  console.warn(doc.data.title);
  return;
}

Note that these documents do have an excerpt in the output meaning that every document has the templateContent property at one point. Maybe something is processed twice in the pagination logic?

Leaving this closed for now as the issue at hand is resolved.


For reference and if you need it, here is what I’m currently using to generate post excerpts:

module.exports = function (eleventyConfig) {
  // Defines shortcode for generating post excerpts
  eleventyConfig.addShortcode('excerpt', post => extractExcerpt(post));

  // ...
};

const excerptMinimumLength = 140;
const excerptSeparator = '<!--more-->'

/**
 * Extracts the excerpt from a document.
 *
 * @param {*} doc A real big object full of all sorts of information about a document.
 * @returns {String} the excerpt.
 */
function extractExcerpt(doc) {
  if (!doc.hasOwnProperty('templateContent')) {
    console.warn('Failed to extract excerpt: Document has no property `templateContent`.');
    return;
  }

  const content = doc.templateContent;

  if (content.includes(excerptSeparator)) {
    return content.substring(0, content.indexOf(excerptSeparator)).trim();
  }
  else if (content.length <= excerptMinimumLength) {
    return content.trim();
  }

  const excerptEnd = findExcerptEnd(content);
  return content.substring(0, excerptEnd).trim();
}

/**
 * Finds the end position of the excerpt of a given piece of content.
 * This should only be used when there is no excerpt marker in the content (e.g. no `<!--more-->`).
 *
 * @param {String} content The full text of a piece of content (e.g. a blog post)
 * @param {Number?} skipLength Amount of characters to skip before starting to look for a `</p>`
 * tag. This is used when calling this method recursively.
 * @returns {Number} the end position of the excerpt
 */
function findExcerptEnd(content, skipLength = 0) {
  if (content === '') {
    return 0;
  }

  const paragraphEnd = content.indexOf('</p>', skipLength) + 4;

  if (paragraphEnd < excerptMinimumLength) {
    return paragraphEnd + findExcerptEnd(content.substring(paragraphEnd), paragraphEnd);
  }

  return paragraphEnd;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants