TypeScript Webpack Setup (with CSS Modules)
It has not been many days since I started to choose TypeScript instead of JavaScript. It's an interesting language(or a superset, as many of us would like to call) which adds so much awesomeness to Vanilla JavaScript. The most notable feature it adds to JavaScript is the TypeSafe programming.
Type-safe programming simply means that a programmer is less likely to write code which would produce a type-error. Type safety is the core feature of a Statically typed language.
How TypeScript makes JavaScript a type-safe language? Well, it does not. Because when the TypeScript code is compiled, there is no way the JavaScript engine running the compiled code, would prevent type errors. Then what's so special about it? In short, TypeScript makes the development process easier and less tedious.
TypeScript Resources
Since the primary intention for this post is to provide an step-by-step guide on how to set-up a TypeScript project with Webpack, I would not add 'How to code in TypeScript' kind of thing here.
However, if you are curious about learning TypeScript, following are some of the good Resources (also available in my resources repository):
Setting Up the Project
To set-up a TypeScript project with webpack correctly, you should first understand how would everything work all-togather.
If you have manually wrote a webpack configuration before, you would know that webpack simply takes input(for example: SASS, JavaScript etc) and puts them into a single file(or multiple, in some cases).
To answer the question 'Why we even need such overhead for a front-end project', not all browser supports module yet. Plus there are other features like Tree Shaking(fency term for removing dead codes), code-splitting, dependency management etc which comes handy with a bundler like webpack.
To bundle TypeScript source files using Webpack, all we need to do is add a loader which will handle TypeScript-specific files.
There are two ways to achieve this.
ts-loader
ts-loader
is a webpack loader which will
compile TypeScript to regular JavaScript. Note that, ts-loader
depends on
typescript
to compile code using
tsconfig.json
. So, it's pretty much obvious that we must add typescript
as a
dependency to the package.json
file.
If you are using ts-loader
, relevent configuration from webpack.config.ts
would look something like this:
module.exports = {
....
module: {
rules: [
{
test: /\.tsx?$/, // select file ending with .ts or .tsx
use: 'ts-loader', // use ts-loader to handle TypeScript files
exclude: /node_modules/, // don't do anything for node_modules
}
]
}
....
}
That's pretty much it. No other configuration required for TypeScript.
@babel/preset-typescript
@babel/preset-typescript
is a collection of plugins to handle TypeScript files. As of now, this preset
contains only one plug-in called @babel/plugin-transform-typescript
If you are planning to follow this approach (and I will definately recommend
this approach), you should have a .babelrc
file at project-root (same as wher
you have webpack.config.ts
file).
{
"presets": ["@babel/preset-typescript"]
}
Install babel-loader
for webpack. And ask webpack to use babel-loader
to
handle TypeScript files. We are done.
tsconfig.json
Please refer to official TypeScript documentation to learn about the configurations. A minimal configuration file may look like this:
// this is a default tsconfig I use in all of my projects. feel free to modify
// it as you want.
{
{
"compilerOptions": {
"module": "CommonJS",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
},
"exclude": ["node_modules"]
}
}
Adding Types Definition to the Project
When writing webpack configuration file(s) in TypeScript, you may encounter
errors. Don't worry about them. They are easy to ger rid of. Just install
appropriate type definitions from @types
.
For example, to get rid of webpack configuration error, install @types/webpack
using Yarn.
Step by Step Guide
Initialize package.json
. You can use either yarn
or npm
. I would suggest
you to use yarn
, as it's faster. But it's upto you what you use...for sure.
To initialize, simply run yarn init
(or npm init
) to create package.json
.
This will hold information about the project. For exapmple, dependencies etc.
Optionally, you can pass --yes
, which will choose default value for every
question asked by either tools.
Create webpack configuration file, webpack.config.ts
. Note that, I am going to
write it in TypeScript. You may choose to write in plain JavaScript. It does not
matter.
We need to import path
module from Node.js as we have to provide valid and
correct pathnames to configuration options. We also need to install couple of
other dependencies. Following is a list of what we will need.
Primary Packages:
webpack
: Bundles source fileswebpack-cli
: Provides command line interface for webpackwebpack-dev-server
: Runs a development server for live-reloading changes we make in our project.html-webpack-plugin
: Injects webpack bundles(scripts, stylesheets etc) into HTML file.css-loader
: Handles CSS filesmini-css-extract-plugin
: CSS modules are awesome! This will parse and extract CSS into a cachable file. You can youstyle-loader
if you prefer to embed styles using<style>
tag.typescript
: compiles TypeScript into JavaScript. Also compiles webpack configuration file.ts-node
: for running webpack config filets-loader
: For handling TypeScript files
Types:
Following packages provides type definitions for TypeScript. We need these packages beause some of the libraries does not provide official type definitions yet.
@types/webpack
@types/webpack-dev-server
@types/mini-css-extract-plugin
Note that these packages should be installed as devDependencies
. No problem if
you wish to keep them as dependencies
. Just a convention.
Install all of them using yarn
:
yarn add --dev webpack webpack-dev-server html-webpack-plugin
mini-css-extract-plugin css-loader ts-loader typescript @types/webpack
@types/webpack-dev-server @types/mini-css-extract-plugin
And copy the following webpack.config.ts
file. Configuration options are
pretty much self-explanatory. You can always look up for explanation
import * as path from "path";
import * as webpack from "webpack";
import * as HtmlWebpackPlugin from "html-webpack-plugin";
import * as MiniCssExtract from "mini-css-extract-plugin";
const config: webpack.Configuration = {
mode: process.env.NODE_ENV === "prod" ? "production" : "development",
entry: path.resolve(__dirname, "src/index.ts"),
output: {
path: path.resolve("build"),
filename: "[name].[contenthash].js",
},
// plugins
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "public/index.html"),
}),
new MiniCssExtract(),
],
// modules and loaders
module: {
rules: [
{
test: /\.tsx?$/,
use: ["ts-loader"],
},
{
test: /\.css$/,
use: [MiniCssExtract.loader, "css-loader"],
},
],
},
// dev server config
devServer: {
port: 3000,
contentBase: path.resolve(__dirname, "build"),
compress: true,
},
};
export default config;
At this point, Webpack configuration is done. Whatever webpack bundles, will be
placed inside build/
directory. You can safely .gitignore
this directory.
To make development process easier, you may add npm scripts in package.json
.
Following are two example scripts:
{
...
"scripts": {
"start": "webpack s",
"build": "rimraf build && webpack"
}
...
}
Note that in build
script I have added rimraf
. This is a package provides
functionalities of rm
command. You can simply install it using yarn add --dev rimraf
.
Purpose of using it, is to remove build/
directory to get a fresh
build of the source code everytime we run webpack
.
Troubleshooting
TS2307: Cannot find module '*.css' or its corresponding type declarations.
This occurs because TypeScript have no idea how to handle imported CSS. To get
rid of this, create a file called declaration.d.ts
in project root and put
following code.
declare module "*.css" {
const content: Record<string, string>;
export default content;
}
With Babel
Pretty simple. Instead of loading TypeScript file with ts-loader
, use
babel-loader
. You can use @babel/preset-typescript
for a ready-made
configuration.
Conclusion
I put up a repository on GitHub which contains the code I have demonstrated in this post. Repository is available at https://github.com/abdus/webpack-typescript-starter
Thank you!