Just a few years ago I was fighting a tough battle with the typescript compiler, but now I can't live without it!
Now I write everything in typescript, including the project's build scripts, and some of my Node.js project's build system use rollup, which fortunately supports configuration files written in typescript:
// rollup.config.ts
import { defineConfig } from 'rollup'
import esbuild from 'rollup-plugin-esbuild'
export default defineConfig({
input: ['./src/index.ts'],
output: {
format: 'esm',
dir: './dist',
},
plugins: [esbuild()],
})
Install esbuild
:
npm i esbuild rollup-plugin-esbuild -D
You can use rollup plugins to transpile its own config file too, since we're already using esbuild to compile the project files, why not use it to compile the rollup configuration files as well? It's possible thanks to the --configPlugin
flag:
rollup -c rollup.config.ts --configPlugin esbuild
For more advanced usages, you may use Rollup's Node.js API or other bundlers like esbuild to write a custom build script. In this case, you can use esbuild-register
to run your build script.
This is an example build script, build.ts
:
import { rollup, RollupOptions, watch } from 'rollup'
type Options = {
//...
}
const createMultipleConfigs = (options: Options[]): RollupOptions[] => {
return [
{
//...
},
]
}
const main = async () => {
const configs = createMultipleConfigs({})
await Promise.all(
configs.map(async (config) => {
if (process.argv.includes('--watch')) {
watch(config).on('...')
} else {
const bundle = await rollup(config)
await bundle.write(config.output)
}
})
)
}
main().catch((error) => {
console.error(error)
process.exit(1)
})
Install esbuild-register
:
npm i esbuild-register esbuild -D
And run:
node -r esbuild-register ./build.ts
You can also use sucrase-register
, @swc/register
, or other utilities that hijack the require
function to replace esbuild-register
.
In a monorepo, you can make your build script a dev dependency of the packages that use it, for example, I usually have a pnpm workspace config like this:
packages:
- packages/*
- playground/*
- scripts
The scripts
package is used to group all dev/build scripts, for example, it usually has a release.ts
and build.ts
for releasing packages and build packages respectively.
In scripts/package.json
, I will expose those scripts in the bin
file:
{
"name": "scripts",
"bin": {
"release": "./release.ts",
"build": "./build.ts"
}
}
In order to make these TypeScript scripts executable, I add a hashbang to the top of those files:
#!/usr/bin/env node -r esbuild-register
Now you can add the scripts
package to other workspace packages using the workspace:*
specifier in package.json
:
{
"name": "some-package-you-want-to-use-scripts",
"version": "0.0.0",
"scripts": {
"build": "build",
"release": "release"
},
"devDependencies": {
"scripts": "workspace:*"
}
}
As your organization grows, when it reaches a point where these scripts can be used in other repos too, you can even publish the scripts
package as well.
Read other people's code! For example, large projects like React, Vue, or Vite, projects you are interested in, or projects similar to the one you are building!