Bundling your TypeScript Node.js modules into a single file with Webpack

This is a solution applicable in the context of using TypeScript in a project that runs in Node.js as apposed to in the browser. Additionally, it also covers exporting your code as a library to be imported by an external tool.

Say having your code spread across multiple files and directories is not an option. The code needs to all be in one single file.

For example, one of the following might apply:

  • You can’t export to AMD or System.js module systems to run your bundled code.
  • The production environment flattens the directory structure of your code before execution.
  • You just want to simplify the code before deployment.

Searching around the web, Ive found TypeScript supports exporting to a single file using module systems AMD, and System.js or no module system if you’ve written your code using TypeScript’s internal modules.

This requires adapting your existing code to work with a new module system instead of using Node.js’ existing common.js modules.

We can use webpack to bundle into the common.js module format natively compatible with Node.js.

To get started, your workspace might look something like this

src // TypeScript source
common
utils.ts
lib.ts
server
index.ts
controller.ts
build // JavaScript output compatible with Node.js
common
utils.js
lib.js
server
index.js
controller.js
tsconfig.json
package.json
tests +

Ideally you want a single file bundle that looks like this:

src
...
build
...
bundle
bundle.js // single file
tsconfig.json
...

To do this, install webpack and it’s cli, additionally webpack-node-externals is used here to exclude node_modules from webpack’s bundle.

$ npm install --save-dev webpack webpack-cli webpack-node-externals

Your tsconfig.json and and webpack.config.js should look something like the following:

Add webpack to your package.json scripts

{
...
"scripts": {
...
"bundle:server": "webpack",
...
}

Sidenote: libraryTarget: 'commonjs’ can be used here when your server code exports it’s main function (or class) to be run by an external framework instead of being self starting. See Exposing Via Object Assignment.

Optionally:

Use the tool concurrently to run tsc and webpack in watch mode in the same terminal. This will recompile your typescript source code and rebundle it automagically when changes are made in the TypeScript src folder.

npm install concurrently --save-dev

then in your package.json scripts:

{
...
"scripts": {
...
"watch-compile:server": "tsc --watch",
"watch-bundle:server": "webpack --watch",
"concurrent-watch": "concurrently npm:watch-*"
...
}

Instead of adding each watch based command by name, concurrently supports using a wildcard to target all matching package.json scripts commands.

Typescript Monorepo Support

If you want to build multiple outputs from a single source while maintaining type checking and dead code elimination, a good place to start is to make your tsconfig.json noEmit then extending that tsconfig for each output.

Related answer on StackoverFlow

Each tsconfig output would have separate scripts watch commands that can then all be matched by concurrently.