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

Custom Passthrough Copy? #1140

Open
CalvinAllen opened this issue May 1, 2020 · 12 comments
Open

Custom Passthrough Copy? #1140

CalvinAllen opened this issue May 1, 2020 · 12 comments
Labels
education feature: 🗄 passthrough copy Related to the passthrough file copy feature

Comments

@CalvinAllen
Copy link

CalvinAllen commented May 1, 2020

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
Copy link

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
Copy link
Contributor

nhoizey commented Jun 19, 2020

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
Copy link

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
Copy link
Contributor

nhoizey commented Jun 19, 2020

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

@victornpb
Copy link

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

@arrowtype
Copy link

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.

@TigersWay
Copy link
Contributor

* 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
Copy link

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 (TigersWay/eleventy-classic-blog-starter#1).

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

@rolbr
Copy link

rolbr commented Jan 25, 2021

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
Copy link

Brixy commented Jul 1, 2021

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
Copy link
Contributor

nhoizey commented Jul 7, 2021

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

@zachleat
Copy link
Member

Related #379

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
education feature: 🗄 passthrough copy Related to the passthrough file copy feature
Projects
None yet
Development

No branches or pull requests

9 participants