Skip to content

Instantly share code, notes, and snippets.

@danieldietrich
Last active February 14, 2024 13:15
Show Gist options
  • Save danieldietrich/999abe1aaee11dcdf91d182807f7ee3f to your computer and use it in GitHub Desktop.
Save danieldietrich/999abe1aaee11dcdf91d182807f7ee3f to your computer and use it in GitHub Desktop.
The easiest way to bundle a simple TypeScript web application

THIS README IS OUTDATED AND UNMAINTAINED - PLEASE DON'T RELY ON THIS

The easiest way to bundle a simple TypeScript web application

Packaging JavaScript applications can be a bit overwhelming. The popular project uglifyjs does not support ES6, it is cumbersome to configure the allmighty Webpack, bundlers like Parcel and Microbundle still have bugs or do not compile to ESM bundles that work in a browser. It is hard to figure out the best way to bundle an application.

Here I give a small example, how we achieve the goal using the

☕️ Prerequisites: Install Node.js

✨ Feature overview

  • Zero configuration bundling (esm, umd)
  • Proper entry point hints for npm, unpkg and when used as module
  • Runs with <script type='module'> in a web page

🚀 Initialize Node.js project

Go to a new directory say-hello/

npm init -y

Install dependencies

npm i -D typescript rollup terser

Replace package.json "scripts" (umd name sayHello is app specific)

"scripts": {
  "clean": "rm -fr dist",
    "build": "npm run clean && npm run lint && tsc --project tsconfig.build.json && npm run bundle:esm && npm run bundle:esm:min && npm run bundle:umd && npm run bundle:umd:min && npm run build:stats",
    "build:stats": "(echo '\\033[35;3m' ; cd dist && ls -lh index*js index*gz | tail -n +2 | awk '{print $5,$9}')",
    "bundle:esm": "rollup dist/index.js --file dist/index.mjs --format esm",
    "bundle:esm:min": "terser --ecma 6 --compress --mangle --module -o dist/index.min.mjs -- dist/index.mjs && gzip -9 -c dist/index.min.mjs > dist/index.min.mjs.gz",
    "bundle:umd": "rollup dist/index.js --file dist/index.umd.js --format umd --name sayHello",
    "bundle:umd:min": "terser --ecma 6 --compress --mangle -o dist/index.umd.min.js -- dist/index.umd.js && gzip -9 -c dist/index.umd.min.js > dist/index.umd.min.js.gz",
},

Replace "main" in package.json

- "main": "index.js"
+ "main": "dist/index.js",
+ "module": "dist/index.min.mjs",
+ "unpkg": "dist/index.umd.min.js",
+ "types": "dist/index.d.ts",
+ "files": [
+   "dist"
+ ],

Create TypeScript configuration tsconfig.json

{
  "compilerOptions": {
    "declaration": true,
    "lib": ["es6", "dom", "dom.iterable"],
    "module": "es6",
    "moduleResolution": "node",
    "removeComments": true,
    "sourceMap": true,
    "strict": true,
    "target": "es6",
    "outDir": "./dist"
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "dist",
    "node_modules"
  ]
}

Create test application src/index.ts

export function sayHello() {
    return "Hi ya all!";
}

Create a file example/index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Say Hello</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="author" content="Daniel Dietrich">
    </head>
    <body>
        <noscript>
            You need to enable JavaScript to run this app.
        </noscript>
        <div id="app">
            Loading...
        </div>
        <script type="module">
            import { sayHello } from '../dist/index.mjs';
            document.getElementById('app').innerHTML = sayHello();
        </script>
    </body>
</html>

📦 Build the project

npm run build

Bundle

🥰 Directly using an ESM bundle in a web page

Loading our example web page example/index.html (might require to Disable Local File Restrictions in the browser)

Example

The interesting part

<script type="module">
    import { sayHello } from '../dist/index.mjs';
    document.getElementById('app').innerHTML = sayHello();
</script>

When published to npmjs.com, we can use the unpkg.com CDN to include our module in a web page

<script type="module">
-   import { sayHello } from '../dist/index.mjs';
+   import { sayHello } from 'https://unpkg.com/say-hello?module';
    document.getElementById('app').innerHTML = sayHello();
</script>

[Written by @danieldietrich]

@idkjs
Copy link

idkjs commented Jun 22, 2021

Thank you for sharing this gist, @danieldietrich. To get it going out of the box, you have to handle your lint command. I did so with npm i --save-dev typescript-eslint/eslint-plugin typescript-eslint/parser and copying these files: eslintrc.js and tsconfig.build.json.

I figured you know what you are doing. This works but is surely not correct so maybe have a look. Thank you, sir!

Run with npx parcel serve example/index.html

example

@unicomp21
Copy link

Love it!!!

@jordanbtucker
Copy link

What about sourcemaps?

@danieldietrich
Copy link
Author

Good point @jordanbtucker! This one is outdated… need to update it when I find some time…

@s17-git
Copy link

s17-git commented Dec 2, 2022

what about snowpack ?

@danieldietrich
Copy link
Author

Good point @s17-git! I also think this README is outdated. In fact I currently either use vite or microbundle. Both are superb. Haven't tested snowpack, yet.

@DerZyklop
Copy link

@danieldietrich
Copy link
Author

Thanks @DerZyklop, this README is totally outdated. Please don't rely on it anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment