/snowpack-preact-ts

snowpack + Preact + TypeScript

Primary LanguageTypeScript

View Demo

Michael Scott’s Dunder Mifflin Scranton Meredith Palmer Memorial Celebrity Rabies Awareness Pro-Am Fun Run Race For The Cure

snowpack + Preact + TypeScript

Example using TypeScript, Preact, and snowpack, a new ESM bundler. Uses [Sass][sass] for styling.

Other Languages

Example

npm i
npm start

It’ll run a multi-page app at localhost:5000.

Note: changes to .tsx & .scss files will re-build automatically, but changes to src/index.html will require a restart of the dev server

💁 Explanation

Type handling is the difficult part of using TypeScript with snowpack, since browsers don’t support it. The core concepts of pulling this off are:

  1. Having a separate src/ and dist/ folders
  2. Using this fix in tsconfig.json (discussion):
      "compilerOptions": {
        "paths": {
          "/web_modules/*.js": [
            "node_modules/@types/*",
            "node_modules/*",
            "web_modules/*.js",
          ]
        }
      }

The first point is a rather obvious one: since TypeScript isn’t supported by browsers, we’ll have to keep our source code separate from our compile directory. That’s easy enough, but when you compile it, now you have a lot of these in your code:

import preact from "preact";

A browser doesn’t know what to do with that! That’s not a proper path, and it’s also missing the .js extension. However, if you try modifying your TypeScript to:

import preact from "./web_modules/preact.js";

Now TypeScript can’t resolve those modules or their types! However, by aliasing these imports a special way, we can do something clever:

  "compilerOptions": {
    "paths": {
      "/web_modules/*.js": [
        "node_modules/@types/*",
        "node_modules/*",
        "web_modules/*.js",
      ]
    }
  }

This lets us do the following in our project (we’ll always need to do this for all web_modules):

import preact from "/web_modules/preact.js";

Let’s break down what this tells TypeScript:

  • /web_modules/*.js tells TypeScript to take any import matching this pattern and treat it differently (“this pattern” meaning any import that starts with a /, then has web_modules, then anything (*), then ends in .js).
  • How should TypeScript treat them differently? Well, it should try and find the * in any of the following folders:
    • First, try node_modules/@types/* (for the modules that have external types)
    • Next, try node_modules/* to see if the original library has types
    • Lastly, try web_modules/* to see if snowpack ported the types over.

Usually, the types will be found somewhere within node_modules and TypeScript is happy.

But wait—what about browsers? Well that’s the best part! If we look at our output code:

import preact from "/web_modules/preact.js";

That /web_modules/preact.js resolves to be an actual path to our ESM on our server (assuming web_modules was installed at the root)! So the alias we were using for TypeScript also happened to be a valid path the browser understands.

If you need to install web_modules somewhere else other than the root, then you can change the --dest of snowpack as well as the option under "paths" in tsconfig.json as-needed.


If you’re using ESLint, you’ll have to add a few more rules to keep it happy (you may already be familiar with this if you’ve used TypeScript & JSX before):

{
  "rules": {
    "import/extensions": "off", // we need this for browsers
    "import/no-absolute-path": "off", // we also need this
    "import/no-unresolved": "off", // TypeScript got this
    "react/jsx-filename-extension": [
      "error",
      { "extensions": [".js", ".jsx", ".tsx"] }
    ]
  }
}