11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
16.84k stars 487 forks source link

Custom Passthrough Copy? #1140

Open CalvinAllen opened 4 years ago

CalvinAllen commented 4 years ago

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!

denisbrodbeck commented 4 years ago

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);
nhoizey commented 4 years ago

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.

denisbrodbeck commented 4 years ago

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 😅

nhoizey commented 4 years ago

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

victornpb commented 3 years ago

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

arrowtype commented 3 years ago

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:

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.

TigersWay commented 3 years ago
* 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!

arrowtype commented 3 years ago

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!

rolbr commented 3 years ago

Got here by looking for a way to copy a central, shared asset folder to each output folder. Anyone got an idea and is willing to nudge me in the right direction? Goal is to create self-contained output folders that contain no relative links leading outside of that folder, allowing the folder to be moved around at will.

Brixy commented 3 years ago

Sorry for bringing this up again.

I have transferred multiple projects to Eleventy—and it was a joy!

Yet, some teaching projects (made with Jekyll or Hugo) have a structure like the one mentioned in @CalvinAllen’s initial post with dozens of files in each article folder.

For these projects Eleventy does not seem an option, yet, although keeping articles and assets together in one folder makes perfect sense and is supported by many SSGs.

Would it make sense to address this issue before a 1.0 release of Eleventy? This would allow to switch projects to Eleventy without changing a complex folder structure.

nhoizey commented 3 years ago

@arrowtype a late answer:

  • 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.

I disagree. I love to see dates in URLs, as it allows me to decide if I want to load the page or forget it because it's too old.

I also love being able to navigate "up" in the content hierarchy by removing the article slug from the URL and having the list of content from the same month.

For example, if I'm in https://nicolas-hoizey.com/articles/2018/07/02/leaving-500px/ , I can remove leaving-500px/ from the URL in the browser, and get all articles from July 2018 (I don't write enough these days… 😅) or remove 07/02/leaving-500px/ to get all articles from 2018.