bp

Typescript Eleventy Config

disabled programmer typing at a computer, the keyboard is at his fingers. he has a huge smile on his face, the screen is glowing brightly, butterflies and small birds are flying circles around the crt monitor

There are a bunch of guides out there explaining how to write 11ty configs in typescript so that you can compile them prior to running 11ty. This isn't one of those. I'm here to explain how to run eleventy on your typescript configs, with all the familiar editor ergonomics, without building your config files at all. Dev time, meet runtime.

You'll need node.js 22.6 or later, which adds the --experimental-strip-types flag. What this does is tell node to just ignore typescript syntax, as if it was a comment. Node here is basically implementing the types as comments proposal, and I hope everyone else follows suit quickly.

Doing the Cool Kids' Homework

Now, 11ty follows the types-in-jsdoc approach, which is fine i guess whatever, but for some reason, they don't then ship those types in .d.ts files, so we'll have to build them ourselves. Run this script in postinstall:

import { $ } from 'execa';

import Manifest from '../package.json' with { type: 'json' };

await $`npx tsc node_modules/@11ty/eleventy/src/UserConfig.js
        --declaration
        --allowJs
        --emitDeclarationOnly
        --moduleResolution nodenext
        --module nodenext
        --target esnext`;

for (const pkg in Manifest.dependencies) {
  if (pkg.startsWith('@11ty')) {
    const spec = import.meta.resolve(pkg).replace('file://', '');
    try {
      await $`npx tsc ${spec}
              --declaration
              --allowJs
              --emitDeclarationOnly
              --moduleResolution nodenext
              --module nodenext
              --target esnext`;
    } catch(e) {
      console.log(e.stdout);
    }
    console.log(`Wrote types for ${pkg}`)
  }
}

Shipping .d.ts type declarations in your npm library is an accessibility issue. Your users with cognitive disabilities like ADHD can't afford to keep alt-tabbing back to your docs site. I hope the 11ty authors will integrate this into their build system soon.

TypeScript, Ho!

That having been accomplished, rename your config file and import the UserConfig type:

mv eleventy.config.js eleventy.config.ts
import type { UserConfig } from '@11ty/eleventy';

export default function(eleventyConfig: UserConfig) {
  eleventyConfig.
}

And watch those LSP-completion suggestions roll in.

If you have split your config into multiple files, you need to enable .ts extension in your tsconfig.json. Also disable emit, because we're not actually running tsc at any point, this is all for neovim.

Here, crib mine:

{
  "compilerOptions": {
    "module": "NodeNext",
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "NodeNext",
    "allowImportingTsExtensions": true,
    "target": "ESNext",
    "noEmit": true
  }
}

Last thing is to actually run it, and to do that we'll use the NODE_OPTIONS environment variable

"scripts": {
  "build": "NODE_OPTIONS='--experimental-strip-types' eleventy --config=eleventy.config.ts --incremental",
  "start": "NODE_OPTIONS='--experimental-strip-types' eleventy --config=eleventy.config.ts --serve --incremental"
},

Gimmie the Code

And that's pretty much it! I accomplished the above on this site in commit 307e5cf8, which you're welcome to ogle over at Microsoft's loss-leader code scraping utility.