Eleventy: How to exclude files programmatically from build output?

Created on 26 Apr 2020  Â·  10Comments  Â·  Source: 11ty/eleventy

I include files in two collections called posts and notes as follows:

const isProduction = process.env.NODE_ENV === 'production' ? true : false;
...
config.addCollection("posts", collection => {
    return collection.getFilteredByGlob("_posts/*.md").filter(p => {
        if (isProduction) {
            if (!p.data.draft) return true;
            else return false;
        }
        return true;
    }).reverse();
});

I call it in their respective templates as follows:

{% for post in collections.posts %}

...and this returns collections correctly based on whether it's the production environment or not, and a draft or not.

No post in the directory _posts carries the tag posts and no note in the directory _notes carries the notes tag. In fact, no page anywhere carries any of these tags. These are custom collections as I want complete control over the criteria for their inclusion.

When developing locally or on staging, I want the draft posts and notes to show in the listing.
When published, I do not want them in the listing.

Today, I found out that they were getting built and included the entirety of the past month and were viewable on my production/live/published site, should one know what URL to hit.

How do I exclude these items from build (and not just their respective collections) on a per environment basis?

Environment:

  • OS and Version: Pop!_OS 19.10
  • Eleventy Version: 0.10.0
education

Most helpful comment

Wondering if a "Directory Data File" with a calculated permalink (with EleventyComputed) would help?

All 10 comments

Idea 1

I checked the docs at https://www.11ty.dev/docs/ignores/, but it doesn't allow you to programmatically exclude files. I'd prefer not to use fs to write file paths on each build to .eleventyignore; maintaining inclusions and exclusions this way feels like a dirty solution. addCollection() method also doesn't have a callback so I don't think it's possible for me to build such a list at build-time anyway. It would also require a re-build after .eleventyignore gets updated for those files to actually get excluded.

Idea 2

896 suggests using permalink: false - which sounds like a good idea in theory. I am however using a CMS like Netlify CMS/Forestry in front of eleventy. Such a UI requires that the value be either true or false. Even if I change the field type to just text instead of boolean, it's not a viable solution. See below.

I _could_ replace draft: true with permalink: false in the file metadata, switching it to true in the CMS user interface will trigger a build error, as expected and as tested.

Another dirty solution is to leave permalink empty, as I suggested above, in the frontmatter as a means to say don't exclude this and set it to false as a way of saying exclude this file from build.
This solution also seems to break permalinks...

  • For posts that are not draft in the collection posts, the constructed permalinks equal the directory structure, ignoring the value in the layout frontmatter, which is expected.
  • It will also not allow me to navigate to draft posts in my development or staging environments - draft posts which are included in the posts collections in these environments have no permalink, as its set to false!

This feels like it is something that will require work at the static site generator level. Perhaps the ability to specify and read a particular front-matter field? Something similar to eleventyExcludeFromCollections but to exclude completely from build instead.

Have you considered environment variables for development and production?

@reubenlillie Thanks for the response. How do you suggest I could use an environment variable for this? The goal as per the docs is to be able to expose those variables to the template files. I'm not sure I understand how that could help me achieve what I need.


I've also had some further thought on this. One sane way to achieve my original goal would be to:

  • check the ELEVENTY_VAR env in the .eleventy.js file
  • call a _new_ method while building a custom collection
  • this could be eleventyConfig.addIgnoreTarget(item.inputPath), for example

I'm not sure how flexible this would be.

The problem with a frontmatter key value such as eleventyExcludeFromBuild, is that it doesn't allow enough freedom in its default behaviour. In fact, it could be undesirable for certain people.
For example, someone might want the file to be excluded even from development and staging builds and not just production builds.
Someone else, like me, would want eleventy to ignore the frontmatter key and include the file in build if in development environment but exclude from production environment.

This could add confusion, whereas a configuration method feels more suited to this purpose.

Wondering if a "Directory Data File" with a calculated permalink (with EleventyComputed) would help?

Looks like EleventyComputer is slated for v0.11.0. It might be something worth a try. I've got a rough code block written but will test in practice once the next version is released. Might be tricky as I've got YAML front matter on all my files.

I think the permalink: false should work in Netlify CMS, you can make it hidden so users can't edit it. Otherwise you could add a folder data file with it and that should work.

@hirusi You can already try a beta (for now, version 4) and report back. This way a fix might make it into a stable 0.11.0.

I've installed 0.11.0-beta.4 locally, and attempted to try and get the result I'm looking for.

In the _posts directory, I have created a new file named _posts.11tydata.js with the following content. It holds about 25 Markdown files with YAML front-matter. All the files use the post layout.

const getPermalinkForPost = (draft) => {
    if (process.env.ELEVENTY_ENV !== "production") {
        // Not production.
        // All files get included in build. Set permalinks properly for each item type. No need to check draft status.
        // ! For testing sake, checking if permalink actually gets set to false and file excluded.
        return draft ? false : "/blog/{{ data.date | date: '%Y/%m/%d' }}/{{ data.slug }}/";
    } else {
        // Production.
        // Exclude draft items from build by setting permalinks to false.
        return draft ? false : `/blog/{{ data.date | date: '%Y/%m/%d' }}/{{ data.slug }}/`;
    }
};

module.exports = {
    eleventyComputed: {
        permalink: data => getPermalinkForPost(data.draft)
    }
};

For what its worth, the post layout defines its permalink in its YAML front-matter as:

permalink: "/blog/{{ page.date | date: '%Y/%m/%d' }}/{{ slug }}/"

Upon running eleventy from the command line, I receive the following error and can't make sense of how it's writing duplicate permalinks.

> Output conflict: multiple input files are writing to `./_site/blog/index.html`. Use distinct `permalink` values to resolve this conflict.
[ file list here]
`DuplicatePermalinkOutputError` was thrown:
    (Repeated output has been truncated…)
        at TemplateMap.checkForDuplicatePermalinks (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/TemplateMap.js:547:13)
        at TemplateMap.cache (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/TemplateMap.js:308:10)
        at async TemplateWriter._createTemplateMap (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/TemplateWriter.js:170:5)
        at async TemplateWriter.writeTemplates (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/TemplateWriter.js:203:5)
        at async TemplateWriter.write (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/TemplateWriter.js:254:25)
        at async Eleventy.write (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/Eleventy.js:742:13)
        at async Eleventy._watch (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/Eleventy.js:489:5)
        at async Timeout._onTimeout (/home/ru/dev/rusingh.com/web/node_modules/@11ty/eleventy/src/Eleventy.js:679:11)
Copied 1 file in 2.19 seconds (v0.11.0-beta.4)
Watching…

All the files that complain of duplicate permalinks appear to be the ones which have their draft front-matter set to false and should be built. Along with one file which is the blog index page.

Any pointers?

I'm constructing the permalink using JavaScript itself now, instead of Liquid, and that works. I'm surprised that Liquid variables itself don't seem to work. Is that intended? (1)

Here's the rewritten permalink function:

const getPermalinkForPost = (data) => {
    const { format } = require("date-fns");
    let postPermalink =
        `/blog/${format(data.date, 'yyyy/MM/dd')}/${data.slug}/`;
    if (process.env.ELEVENTY_ENV !== "production") {
        // Not production.
        // All files get included in build. Set permalinks properly for each item type. No need to check draft status.
        // ! Testing on development
        return data.draft ? false : postPermalink;
    } else {
        // Production.
        // Exclude draft items from build by setting permalinks to false.
        return data.draft ? false : postPermalink;
    }
};

Returning false sets the permalink to /blog/false instead of getting excluded from build. Is it getting passed on as a string instead of a boolean value? How do I check this? (2)

Scratch that - I got this working. eleventyComputed wins! :)

While testing the permalink function in the data directory file on the development environment, I forgot to take a look at the addCollection method in the eleventy configuration file and make sure it is also excluding draft posts from the collection itself on development environment. So, really, draft blog posts getting included in the blog index page was purely a UI issue.

Testing on production environment, everything works well.

However, still wondering why Liquid variables didn't work. @zachleat - any idea on that?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zac-heisey picture zac-heisey  Â·  3Comments

zachleat picture zachleat  Â·  3Comments

nebrelbug picture nebrelbug  Â·  3Comments

aaronstezycki picture aaronstezycki  Â·  3Comments

zellwk picture zellwk  Â·  3Comments