Eleventy: Custom Passthrough Copy?

Created on 1 May 2020  Â·  8Comments  Â·  Source: 11ty/eleventy

Is it possible to customize the pass-through copy when the output directory is NOT the same as the source directory when using explicit permalinks?

I have a Jekyll blog I'm working on converting, but I'm running into an issue (one that I solved with a custom Jekyll plugin), and my structure looks like this:

/_posts/
   /2020/
       /2020-04-30-some-blog-post-title/
            ./2020-04-30-some-blog-post-title.md
            ./cover.png

Dates are not part of my permalinks, which ends up only being the 'some-blog-post-title'.
The custom plugin I have copies that 'cover.png' to the proper output folder. I end up with something like this:

/_site/
    /some-blog-post-title/
        /index.html
        /cover.png

Coming to Eleventy, I have to specify a slug + permalink to get the output structure the same (okay, no big deal), but with all the normal passthrough options, I end up retaining the dated folders from the original.

/_site/
    /_posts/
        /2020/
            /2020-04-30-some-blog-post-title/
                /cover.png
    /some-blog-post-title/
        /index.html

I want to, somehow, use the source directory during the build combined with the permalink of the item its building, to copy the specific assets along with the rendered output.

Any way to do this?

Thanks!

education

All 8 comments

I had a similar requirement.

In the end I created several utility scripts, which enabled me to put images next to the related post. These images could be accessed via a custom media(page) filter, which rewrote the images path to the final path. Another script copied all assets from src/posts/.

With this directory layout:

src/posts/
         /some-title/
                    /index.html
                    /cover.png
                    /image.jpg
         /good-title/
                    /index.html
                    /cover.png

The posts get resolved this way:

dist/posts/
          /some-title/
                     /index.html
          /good-title/
                     /index.html

And the assets get copied this way:

dist/assets/media/
                 /some-title/
                            /cover.png
                            /image.jpg
                 /good-title/
                            /cover.png

In order to resolve the (relative) images, an extra filter is needed:

// .eleventy.js
module.exports = function (config) {
  config.addFilter(`media`, (filename, page) => {
    // filename:       'image.jpg'
    // page.inputPath: './src/posts/some-title/index.md',
    // want:           '/assets/media/some-title/image.jpg',
    if (!page.inputPath.split(`/`).includes(`posts`)) {
      return filename;
    }
    const path = require(`path`);
    const subdir = path.basename(path.dirname(page.inputPath));
    return `/assets/media/${subdir}/${filename}`;
  });
  // further config...
}

Call the media(page) filter in your markdown:

// src/posts/some-title/index.md

<figure class="wide">
  <img src="{{ 'image.jpg' | media(page) }}" alt="some blog image" loading="lazy">
  <figcaption>
    Some image caption.
  </figcaption>
</figure>

Use this script to copy the assets from your posts (you might call it with postbuild in package.json.)

// _utils/copy.js

const fs = require(`fs`);
const path = require(`path`);
const fastglob = require(`fast-glob`); // 11ty uses `fast-glob` internally

async function copy() {
  const base = `src/posts`;
  const entries = await fastglob([`**/*.{jpg,jpeg,png,gif,webp,mp3,mp4,webm,ogg}`], { cwd: base });

  for (const entry of entries) {
    const src = path.join(base, entry);
    const dst = path.join(`dist/assets/media`, entry);
    await fs.promises.mkdir(path.dirname(dst), { recursive: true });
    await fs.promises.copyFile(src, dst);
  }
}

copy().catch(console.error);

I had the same issue in the beginning (also coming from Jekyll and the jekyll-postfiles plugin), and instead of adding computing to get the expected result, I chose to adapt to Eleventy's default behavior as much as possible.

I changed my source folders hierarchy so that it matches the build hierarchy, which means I don't need any permalink, and used the standard addPassthroughCopy for all images.

I had to reorganize my content in the beginning, but now it works perfectly, and i didn't add any complexity or build time.

I had the same issue in the beginning (also coming from Jekyll and the jekyll-postfiles plugin), and instead of adding computing to get the expected result, I chose to adapt to Eleventy's default behavior as much as possible.

I changed my source folders hierarchy so that it matches the build hierarchy, which means I don't need any permalink, and used the standard addPassthroughCopy for all images.

I had to reorganize my content in the beginning, but now it works perfectly, and i didn't add any complexity or build time.

@nhoizey This is a totally fine and a very hassle-free approach, which I very much prefer! But then again comes my client and wants their preferred workflow and that's when one goes down the rabbit hole 😅

@denisbrodbeck indeed, clients not always allow us making simple things… 😅

I just made this plugin to solve this limitation
https://www.npmjs.com/package/eleventy-plugin-page-assets

I had to reorganize my content in the beginning, but now it works perfectly, and i didn't add any complexity or build time.

If you’re making & writing a blog over time, a big problem in matching build output with src input is that you’re forced into one of two unfortunate situations:

  • If src/post paths don’t include dates, they will look okay in permalinks, but will be more unorganized and harder to deal with over time, in a writing workflow.
  • If src/post paths _do_ have dates, then so will permalinks on the website, making these links more ugly and more hostile to visitors, sharing, etc.

Instead, it’s very nice to have dated paths in the src, and custom permalinks for site URLs. This is probably a big reason 11ty supports permalinks in the first place. So, extending that to assets used in posts seems like a natural and important feature.

* If `src/post` paths _do_ have dates, then so will permalinks on the website, making these links more ugly and more hostile to visitors, sharing, etc.

This is not exactly true, as 1 very good point with Eleventy you can do whatever you need!
I have a starter template where I wanted to "solve" that exact problem: ECBS
It's just an idea!

This is not exactly true, as 1 very good point with Eleventy you can do whatever you need!

Part of what makes me interested in 11ty is that this does seem to be the case! However, I’m just somewhat confused on how to do this, as a newcomer.

Placing images next to posts & controlling permalinks seem like things that shouldn’t be too difficult, but I’m not exactly sure where to start. ECBS seems promising, but didn’t build quite as I expected it to (https://github.com/TigersWay/eleventy-classic-blog-starter/issues/1).

Thanks for pointing out that there might be an existing solution!

Was this page helpful?
0 / 5 - 0 ratings