Estructuración del archivo de configuración de Eleventy

Publicado:
Última edición:
Tiempo de lectura:
9 min.

Eleventy viene con algunos valores básicos por defecto. Por ejemplo, la carpeta de salida por defecto es _site, y Eleventy busca tus archivos fuente en el directorio raíz.

Esto está bien para proyectos muy pequeños. Un archivo de configuración adicional no es necesario para trabajar con Eleventy. Sin embargo, soy un gran fan de la estructura, la organización y la claridad, y la mayoría de mis proyectos son bastante grandes. También tengo preferencias personales, y Eleventy es bastante abierto al respecto: puedes organizarlo y llamarlo como quieras.

Empecemos!

Saltar el índice de contenidos

índice de contenidos

Crear un archivo de configuración .eleventy.js

Añade un nuevo archivo en el directorio raíz llamado .eleventy.js (a partir de Eleventy 2.0 también puede llamarse eleventy.config.js.).

Hagamos primero un pequeño ajuste en la estructura de carpetas.

module.exports = function (eleventyConfig) {
  return {
    dir: {
      input: 'src',
      output: 'dist',
      includes: '_includes',
      layouts: '_layouts'
    }
  };
};

Nuestra carpeta de salida es ahora dist, y todos nuestros archivos fuente van a la carpeta src.
También, debido a mi preferencia personal, saco la carpeta layouts fuera de la carpeta _includes, donde normalmente vive y me aseguro de que estén uno al lado del otro.

Esto deja nuestra raíz para todos los archivos que absolutamente tienen que estar allí - como package.json y README.md, así como los archivos de configuración de otros módulos que utilizas en tu proyecto.

Estructurar la carpeta de entrada

Crea una carpeta llamada src en tu directorio raíz.
Aunque no vamos a tocar la mayoría de las carpetas, este es el aspecto que podría tener tu proyecto web habitual:

│
├── src
│  │
│  ├── _data
│  ├── _includes
│  ├── _layouts
│  ├── assets
│  ├── pages
│  ├── posts
│  ├── projects
│

pages es para tus páginas estáticas como index.md, about.md, etc., posts contiene los artículos de tu blog, y projects es sólo otra carpeta de colección que creamos para que valga la pena sacar la lógica de eleventy.js.

… Porque puedes configurar todas tus collections, shortcodes y filters ahí mismo.

Externalización de las personalizaciónes

Quiero que mis proyectos crezcan libremente sin preocuparme de que mi archivo de configuración se vuelva demasiado abarrotado. Así que me ocupo de las personalizaciones en otro lugar e importo sólo el valor de retorno de mis funciones.

Mi preferencia es crear una nueva carpeta en el directorio raíz, llamada config.

Otro método habitual es añadir una carpeta a src con el nombre de _11ty. Encontré esto en el starter de Nicolas Hoizeys pack11ty. Puedes nombrar la carpeta como quieras y ponerla donde quieras.
En este caso, seguiré fingiendo que has creado una carpeta llamada config en tu directorio raíz.

No necesitamos avisar a Eleventy sobre la existencia de esta carpeta. Simplemente la usamos para exportar nuestros valores de retorno e importarlos a .eleventy.js.

Introduzco dos buenas maneras de manejar esto, usando collections como ejemplo.

Método 1: Importar el archivo y hacer un bucle sobre los nombres de las collections

Crea un archivo llamado collections.js en tu carpeta config.
Ahora define todas las collections que quieras usar:

module.exports = {
  posts: function (collectionApi) {
    return collectionApi.getFilteredByGlob('src/posts/**/*.md');
  },
  projects: function (collectionApi) {
    return collectionApi.getFilteredByGlob('src/projects/**/*.md');
  }
};

Tu eleventy.js ahora se ve así:

const collections = require('./config/collections.js');

module.exports = eleventyConfig => {
  // Collections
  Object.keys(collections).forEach(collectionName => {
    eleventyConfig.addCollection(collectionName, collections[collectionName]);
  });

  return {
    dir: {
      input: 'src',
      output: 'dist',
      includes: '_includes',
      layouts: '_layouts'
    }
  };
};

Hacemos un “loop” sobre todas las collections definidas en collections.js y las importamos a nuestro archivo de configuración. Ahora harías exactamente lo mismo para tus collections, shortcodes, filters, etc.

Si quieres ver este método en acción, visita el repositorio público del sitio personal de Ryan Mulligan.

¡Muy ordenado!

Este método tiene la ventaja de producir un archivo de configuración realmente compacto. Sin embargo, hay algo que no me gusta.

Hemos traído estructura en ella, pero también quiero ver lo que se está utilizando en mi proyecto, allí mismo, en mi archivo de configuración.

Quiero ver qué collections estoy definiendo, qué filters, etc. ¡Así que aquí viene el método dos!

Método 2: named exports

En lugar de collections.js crea otra carpeta dentro de config llamada collections, y en ella pon un archivo llamado index.js:

// blog posts
const getPosts = collection => {
  return collection.getFilteredByGlob('src/posts/**/*.md');
};

// projects
const getProjects = collection => {
  return collection.getFilteredByGlob('src/projects/**/*.md');
};

module.exports = {
  getPosts,
  getProjects
};

Las exportaciones nombradas pueden hacerse individualmente o agruparse en la parte inferior. Si se exporta todo al final del módulo, como en el ejemplo de aquí, queda mucho más claro, por lo que naturalmente prefiero este método.

Y dentro de tu eleventy.js:

const {getPosts, getProjects} = require('./config/collections/index.js');

module.exports = eleventyConfig => {
  // Collections
  eleventyConfig.addCollection('posts', getPosts);
  eleventyConfig.addCollection('projects', getProjects);

  return {
    dir: {
      input: 'src',
      output: 'dist',
      includes: '_includes',
      layouts: '_layouts'
    }
  };
};

¡Hecho!

Todo está ordenado y puedo ver de un vistazo lo que estoy importando para este proyecto.

Si hay demasiados filtros, colecciones o códigos cortos, los divido más en sus propias carpetas, por ejemplo sólo los filtros para manejar la fecha en un lugar común. Los bloques más grandes, como los shortcodes de Eleventy Image, tienen su propia carpeta.
Los _values_ exportados se importan primero en el archivo padre index.js y luego se vuelven a exportar juntos para el archivo eleventy.js. 🤪

Método 3: Más archivos de configuración como plugin

Después de compartir este artículo en Mastodon, Zach me indicó que hay aún otra forma de externalizar mis componentes de configuración:

eleventyConfig.addPlugin(require('other-config-file.js'));

No sólo es la notación más compacta, ya que no tengo que importar primero mis return values, sino que tampoco tengo que modificar ningún código: Los archivos de configuración externalizados funcionan igual que el propio elventy.js, devolviendo una función callback. Y puedo ver lo que estoy importando!

Ilustro esto con el ejemplo de una minificación html usando el addTransform incorporado de Eleventy.

En tu eleventy.js:

// ¡nada que importar! :)

module.exports = eleventyConfig => {
  // Transforms
  eleventyConfig.addPlugin(require('./config/transforms/html-config.js'));

  return {
    dir: {
      input: 'src',
      output: 'dist',
      includes: '_includes',
      layouts: '_layouts'
    }
  };
};

Tu html-config.js:

const htmlmin = require('html-minifier-terser');
const isProduction = process.env.ELEVENTY_ENV === 'production';

module.exports = eleventyConfig => {
  eleventyConfig.addTransform('html-minify', (content, path) => {
    if (path && path.endsWith('.html') && isProduction) {
      return htmlmin.minify(content, {
        collapseBooleanAttributes: true,
        collapseWhitespace: true,
        decodeEntities: true,
        includeAutoGeneratedTags: false,
        removeComments: true
      });
    }

    return content;
  });
};

¡Excelente!

A continuación: Passthrough File Copy.

Estructurando Passthrough File Copies

A veces sólo queremos copiar archivos a nuestra carpeta de salida, sin someterlos a más procesos de transformación. Exactamente como están. Así es como entran en juego los “Passthrough File Copies”.

Mantener intacta la estructura de directorios

Supongamos que has almacenado tus fuentes locales en src/assets/fonts.

Si deseas mantener la misma estructura de anidamiento, añade lo siguiente a eleventy,js (he eliminado el código del ejemplo de los métodos de ayuda para mayor claridad):

module.exports = eleventyConfig => {
  // Passthrough Copy
  eleventyConfig.addPassthroughCopy('src/assets/fonts/');

  return {
    dir: {
      input: 'src',
      output: 'dist',
      includes: '_includes',
      layouts: '_layouts'
    }
  };
};

Ahora tus fuentes se copiarán con la misma estructura de directorios, en dist/assets/fonts/.

Normalmente tengo más carpetas en assets que deberían ser copiadas. ¡Hay una manera concisa para esto también!

['src/assets/fonts/', 'src/assets/images/', 'src/assets/pdf/'].forEach(path =>
  eleventyConfig.addPassthroughCopy(path)
);

Colocamos todos los directorios en un array y aplicamos el método forEach() para ejecutar el passthrough una vez por cada elemento del array.

Copiar los archivos a otro directorio

A veces quieres copiar tus archivos a otro directorio. Para mí, esto tiene sentido especialmente para mis variantes de favicon. Puedes decirle al navegador que los busque dentro de una carpeta, pero mi experiencia ha sido que es mejor ponerlos en el directorio raíz de la página web. Sin embargo, no quiero verlos en mi carpeta de entrada (¡demasiado ruido!), así que suelo ponerlos todos en src/assets/images/favicon/.

Para copiar un solo archivo al directorio raíz de dist, escribe:

eleventyConfig.addPassthroughCopy({
  'src/assets/images/favicon/apple-touch-icon.png': 'apple-touch-icon.png'
});

Podrías hacer esto para cada archivo favicon, pero sería una repetición innecesaria. En su lugar, puedes seleccionar todos los archivos del directorio favicon con el * (asterisco) wildcard:

eleventyConfig.addPassthroughCopy({
  'src/assets/images/favicon/*': '/'
});

Por cierto, respecto a los favicons, recomiendo leer este artículo de Andrey Sitnik.

Más trucos de copia de archivos Passthrough

Aquí puedes ser realmente creativo.
Puedes dirigirte a cualquier imagen JPG en cualquier dirección, y enviarlas todas juntas a una carpeta de salida común:

eleventyConfig.addPassthroughCopy({
  '**/*.jpg': 'img'
});

Puedes combinar tus assets en un array, manteniendo la estructura de directorios:

['src/activos/imágenes/', 'src/activos/fonts/', 'src/activos/pdf/'].forEach(ruta =>
  eleventyConfig.addPassthroughCopy(path)
);

O aplicar este interesante filtrado que descubrí en el código fuente del sitio web personal de Robb Knight, donde todo lo que hay dentro de src/assets y src/files se copia en el directorio de salida, excepto los archivos CSS y los archivos que empiezan por guión bajo.

['src/assets', 'src/files'].forEach(path => {
  eleventyConfig.addPassthroughCopy(path, {
    filter: path => !path.endsWith('.css') && !path.startsWith('_')
  });
});

Wrap up

Así es como actualmente estoy estructurando mis proyectos. Puedes ver estos métodos aplicados en mi starter eleventy-excellent. En el repositorio de la página web personal de Miriam Suzanne se puede encontrar un magnífico ejemplo de un Eleventy config perfectamente ordenado.

Siempre merece la pena echar un vistazo al arranque oficial de Eleventy, porque allí se pueden encontrar ideas de vanguardia.

En general, siempre es una gran idea bucear en los repositorios de los starters o en los sitios personales de otras desarrolladoras.

¡Hay tantas ideas geniales por ahí!

Eleventy Meetup

Me invitaron como ponente al Eleventy Meetup Ep. 12 el 16 de marzo de 2023 y di una breve charla basada en este artículo.


Intento mantener mis artículos actualizados y, por supuesto, podría equivocarme o podría haber una solución mejor. Si ves algo que (ya) no es correcto, o algo que debería mencionarse, no dudes en editar el artículo en GitHub.

Webmentions

¿Has publicado una respuesta? Dime dónde: