Eleventy: How to change output directory of some input subdirectory?

Created on 6 Apr 2020  Β·  4Comments  Β·  Source: 11ty/eleventy

Hi and thanks for 11ty :) I'm slowly getting into it since yesterday. I come from metalsmith which allows basically total control of output, at the cost of basically total configuration haha.

My question is: is it possible to match a directoy inside our input directory to another directory when generated in _site? Kinda like the possibility to do this for passthrough copy, but for processed files.

My use case is, I have a content folder inside my input, and I want to define in one place that content is generated in _site instead of _site/content.

I tried to define stuff via the eleventy config, via global data files, directory-specific data files… without luck for now. So I didn't find any way to do this but maybe I'm wrong? Thanks for your help!


_Here are a few more details:_

I want my project's directory structure to totally put apart "content files" and other ones. But I still want some "non-content files" to be processed via 11ty, so I don't want them in the content folder.
Kinda like this:

.
β”œβ”€β”€ _build
β”‚Β Β  └── css.11ty.js
      # ^ this uses _includes/css/style.css and generates _site/css/style.css
      # via 11ty processing and custom permalink
      # (same principle as in https://github.com/philhawksworth/eleventyone)
β”œβ”€β”€ content
β”‚Β Β  β”œβ”€β”€ articles
β”‚Β Β  β”‚Β Β  └── hello-world.md
β”‚Β Β  └── index.njk
β”œβ”€β”€ _data
└── _includes
 Β Β  β”œβ”€β”€ css
 Β Β  β”‚Β Β  β”œβ”€β”€ _article.css
 Β Β  β”‚Β Β  β”œβ”€β”€ _breadcrumbs.css
 Β Β  β”‚Β Β  └── style.css
 Β Β  β”œβ”€β”€ js # this is copied in _site as is via passthrough copy
 Β Β  β”‚Β Β  └── script.js
 Β Β  └── templates
 Β Β      β”œβ”€β”€ layouts
 Β Β      β”‚Β Β  β”œβ”€β”€ article.njk
 Β Β      β”‚Β Β  └── base.njk
 Β Β      └── breadcrumbs.njk

The eleventy config matching this for now is:

module.exports = function(eleventyConfig) {
  eleventyConfig.addPassthroughCopy({"_includes/js": "js"});
  return {
    templateFormats: ["md", "njk", "11ty.js"],
    dir: {
      input: ".",
      includes: "_includes",
      data: "../_data",
      output: "_site",
    }
  }
};

Eleventy generates a _site folder like this:

_site
β”œβ”€β”€ content
β”‚Β Β  β”œβ”€β”€ articles
β”‚Β Β  β”‚Β Β  └── hello-world
β”‚Β Β  β”‚Β Β      └── index.html
β”‚Β Β  └── index.html
β”œβ”€β”€ css
β”‚Β Β  └── style.css # via the processing of _build/css.11ty.js that sets its own permalink
└── js
    └── script.js # via passthrough copy

But I want this:

_site
β”œβ”€β”€ articles
β”‚Β Β  └── hello-world
β”‚Β Β      └── index.html
β”œβ”€β”€ css
β”‚Β Β  └── style.css
β”œβ”€β”€ js
β”‚Β Β  └── script.js
└── index.html
education

Most helpful comment

You can do this with permalinks: https://www.11ty.dev/docs/permalinks/

Eg if you have this:

β”œβ”€β”€ articles
β”‚   β”œβ”€β”€ article-1.md
β”‚   β”œβ”€β”€ article-2.md

By default you get:

β”œβ”€β”€ _site
β”‚   β”œβ”€β”€ articles
β”‚   β”‚   β”œβ”€β”€ article-1/index.html
β”‚   β”‚   β”œβ”€β”€ article-2/index.html

To change the default output /articles/article-i/index.html to just /article-i/index.html, like this:

β”œβ”€β”€ _site
β”‚   β”œβ”€β”€ article-1/index.md
β”‚   β”œβ”€β”€ article-2/index.md

Add a file articles.json inside the articles folder that changes the permalink:

{
  "permalink": "{{ page.fileSlug }}/index.html",

  "layout": "layouts/article.njk", <-- optional
  "tags": ["articles"],            <-- optional
}

That's all!

Here you have the list of the page variable contents (in case you want to use something else than fileSlug): https://www.11ty.dev/docs/data-eleventy-supplied/#page-variable-contentshttps://www.11ty.dev/docs/data-eleventy-supplied/#page-variable-contents

All 4 comments

My "solution" for now is to set content as my input and create a symlink of external folders in it, like this:

module.exports = function(eleventyConfig) {
  eleventyConfig.addPassthroughCopy({"_includes/js": "js"});
  return {
    templateFormats: ["md", "njk", "11ty.js"],
    dir: {
      input: "content",
      includes: "../_includes",
      data: "../_data",
      output: "_site",
    }
  }
};
.
β”œβ”€β”€ _build
β”œβ”€β”€ content
β”‚Β Β  β”œβ”€β”€ .do-not-touch
β”‚Β Β  β”‚Β Β  └── _build -> ../../_build
β”‚Β Β  β”œβ”€β”€ articles
β”‚Β Β  β”‚Β Β  └── hello-world.md
β”‚Β Β  └── index.njk
β”œβ”€β”€ _data
└── _includes

Knowing that the files in _build set their own permalink manually, it works great.

I'd be happy if there was a cleaner way to do this :)

Jekyll may be good inspiration on this one.

In it's config, you can set permalinks for specific collections like so:

collections:
  pages:
    output: true
    permalink: /:path
  implementation:
    output: true
    permalink: /:collection/:path/

A pass-through for processed files sounds like a great idea.

You can do this with permalinks: https://www.11ty.dev/docs/permalinks/

Eg if you have this:

β”œβ”€β”€ articles
β”‚   β”œβ”€β”€ article-1.md
β”‚   β”œβ”€β”€ article-2.md

By default you get:

β”œβ”€β”€ _site
β”‚   β”œβ”€β”€ articles
β”‚   β”‚   β”œβ”€β”€ article-1/index.html
β”‚   β”‚   β”œβ”€β”€ article-2/index.html

To change the default output /articles/article-i/index.html to just /article-i/index.html, like this:

β”œβ”€β”€ _site
β”‚   β”œβ”€β”€ article-1/index.md
β”‚   β”œβ”€β”€ article-2/index.md

Add a file articles.json inside the articles folder that changes the permalink:

{
  "permalink": "{{ page.fileSlug }}/index.html",

  "layout": "layouts/article.njk", <-- optional
  "tags": ["articles"],            <-- optional
}

That's all!

Here you have the list of the page variable contents (in case you want to use something else than fileSlug): https://www.11ty.dev/docs/data-eleventy-supplied/#page-variable-contentshttps://www.11ty.dev/docs/data-eleventy-supplied/#page-variable-contents

The solution from @AlbertVilaCalvo is correct, but works only for folders one level deep, like in the example provided.

If you have deeper folder tree and want to move it up to the tree by dropping one folder, page.fileSlug won't work for you because it return only the slug of the file, not the whole path. The solution is to use page.filePathStem and remove nondesired paths from it.

If you have this folder stucture:

β”œβ”€β”€ content
β”‚   β”œβ”€β”€ articles
β”‚   β”‚   └── hello-world.md
β”‚   └── index.njk

And want instead of this:

β”œβ”€β”€ content
β”‚   β”œβ”€β”€ articles
β”‚   β”‚   └── hello-world
β”‚   β”‚       └── index.html
β”‚   └── index.html

to have this:

β”œβ”€β”€ articles
β”‚   └── hello-world
β”‚       └── index.html

Add a file content.json to the content folder with this content:

{
  "permalink": "{{ page.filePathStem | dropContentFolder }}/index.html"
}

And you have to write the dropContentFolder filter that recieves the full path to the current file and removes /content from it. The quick and dirty implementation can be (in .eleventy.js):

    config.addFilter("dropContentFolder" function (path) {
      const pathToDrop = "/content"
      if (path.indexOf(pathToDrop) !== 0) {
        return path
      }

      return path.slice(pathToDrop.length)
    })
Was this page helpful?
0 / 5 - 0 ratings

Related issues

zachleat picture zachleat  Β·  3Comments

jamrelian picture jamrelian  Β·  3Comments

aaronstezycki picture aaronstezycki  Β·  3Comments

veleek picture veleek  Β·  3Comments

AjitZero picture AjitZero  Β·  3Comments