I will show in this article, step by step, how to configure Webpack to work with TypeScript for front-end application. Webpack is a tool which allows us to code JavaScript or TypeScript front-end applications and divide our code on modules, separated files with view (html), logic (js) and styles (css), and gather all files into single “bundled” html, javascript and css file with its transpilation (e.g. from TypeScript / ES2015+ into ES5) and minification or uglyfication + creating runtime server for development (Webpack Dev Server).
So generally, it provides everything what modern TypeScript or JavaScript project should have on board to make coding easier and faster. Let’s go and get our hands dirty 😉
On our GITLAB HERE you can find full code of our “Webpack configuration with TypeScript” project – also including tutorial from next article about Webpack + TypeScript, which you can read HERE.
Table of Contents
How to use Webpack – short tutorial
At the very begining, remember to install first the NodeJS from its official site.
First we must initiate standard Nodejs project in our directory by opening there a terminal (CMD) and writing command:
- First to get to project’s directory, as example:
cd C:\YOUR_PROJECT_PATH\
- And next init new project here by:
npm init
Next we must install the Webpack in our project:
npm i --save-dev webpack webpack-cli
Next open package.json
file and add there very first part of Webpack configuration:
"scripts": { "start": "webpack --config webpack.config.js" }
Next, of course, we must create webpack.config.js
file in our project root directory which will contain the Webpack configuration details. Next put there that code:
module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, './dist'), filename: 'bundle.js' } }
First we must create app.js file with normal JavaScript code. Further we will start coding in TypeScript. Let’s keep it stupid simple and let’s add there now only:
alert('Hello coders!');
This Webpack config means that it will take app.js file, move that to /dist folder and give the final name bundle.js.
The problem here is that we need to manually create HTML files in dist and link them manually, e.g. at the end of body like:
<!doctype html> <html> <head> <title>APP</title> </head> <body> <script src="bundle.js"></script> </body> </html>
To solve that problem we must install Webpack HtmlWebpackPlugin
plugin and html pages with linked bundle script will be created automatically. Write down in terminal:
npm i --save-dev html-webpack-plugin
and next in webpack.config.js
file we must add on the top of the content the following code:
const HtmlWebpackPlugin = require('html-webpack-plugin');
and add following config property “plugins”:
module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, './dist'), filename: 'bundle.js' }, plugins: [ new HtmlWebpackPlugin({ title: 'Bricks - new APP' }) ] }
and run webpack by command in terminal:
npm run start
and in /dist directory you will see that Webpack has created index.html file linked with bundle.js file.
There is one big assumption in the background – that your JavaScript application to work need to have only empty HTML document with head and body inside it. It means that JavaScritpt (TypeScript will be added later, read further below) application will inject whole its HTML code inside this pure index.html
file. index.html
created by plugin looks like below:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>webpack App</title> </head> <body> <script src="index_bundle.js"></script> </body> </html>
But if you want you can have more advanced HTML code inside generated page, you can inject template there. Check more on official documentation here.
Webpack – Babel configuration
The last thing in this short “How to use Webpack” tutorial are webpack loaders. Loaders take the input files (like JavaScript or TypeScript) and works before bundling by adding some additional changes there. The best example it writing JavaScript code in EcmaScript2015+ syntax or TypeScript and than Webpack’s loaders will do its automatic transpilation into EcmaScript 5 syntax, understandable by all browsers, also Internet Explorer. Let’s follow that example.
First we must install wanted loaders, in this example, Babel:
npm install -D babel-loader @babel/core @babel/preset-env
Next we must add in webpack.config.js files rules, check it below:
module.exports = { entry: './src/app.js', output: { path: path.resolve(__dirname, './dist'), filename: 'bundle.js' }, plugins: [new HtmlWebpackPlugin({ title: 'Bricks - new APP' })], module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }] } }
We can extend app.js file with more sophisticated code within ExmaScript2015+ syntax, and Babel will transpile this code into ES5 syntax:
class Dog { constructor() { this.bark= "Bark bark!"; } say () { alert(this.bark); } } const dogMimi = new Dog (); dogMimi .say();
After that, an alert with a given text “Bark bark!” will be visible in alert box.
TypeScript in Webpack with Babel
To make our Webpack understanding the TypeScript we must install now the TypeScript transpilators. We installed already @babel/preset-env
above for ES2015+ code.
Now, please install TypeScript in your project:
npm install --save-dev typescript
Next we must add in a root directory Webpack TypeScript configuration in the tsconfig.json
file with the following content:
{ "compilerOptions": { "outDir": "./dist/", "noImplicitAny": true, "module": "es6", "target": "es5", "jsx": "react", "allowJs": true, "sourceMap": true }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ] }
Find more about TSCONFIG.JSON file here in official TS DOCS.
And if we want to use in our project both JavaScript and TypeScript code here we have 2 ways to go:
The first way for JS and TS usage is as following:
- Keep
@babel/preset-env
for ES2015+ files and install babel loader
npm install --save-dev @babel/preset-typescript
- Use Babel loader by extending webpack.config.js file as following:
module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.ts$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-typescript'], plugins: ["@babel/plugin-proposal-optional-chaining"] } } }] }
The second way for TS and JS usage is as below:
- Keep
@babel/preset-env
for ES2015+ files and install ts-loadernpm install --save-dev ts-loader
Use TS-loader by extending webpack.config.js file as following:
module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.ts$/, exclude: /node_modules/, use: 'ts-loader', exclude: /node_modules/ }] }
Both, Babel and TypeScript loaders are doing the same work – transpile TypeScript .ts
files into ES5 syntax.
So… since now, we can write our own TypeScript code.
If you want to see whole code in our project just go to our GITLAB HERE.
When installing third party libraries from npm, it is important to remember to install the typing definition for that library for TypeScript. These definitions can be found at TypeSearch. This will allow to have correct suggestions for TypeScript in code editor (e.g. Visual Studo Code).
CSS loader in Webpack
Last needed loader here is CSS + style loaders:
npm install --save-dev css-loader style-loader
It is recommended to use them together – css-loader can simply import CSS code into the JavaScript module. In contrast, style-loader can inject css-loader code into an HTML file using the link or style tags.Let’s extend the modules webpack definition by adding above loaders:
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
Webpack source map config with TypeScript
Next thing in our Webpack configuration with TypeScript is a SOURCE MAP. In final output of ES5 JavaScript files we have minified and uglified files, source map helps modern browsers to “recreate” original set of JavaScript or TypeScript files (modules) and do debugging there. So for debugging we do not use bundled JS files, because it would be very unpractice. So let’s extend the Webpack configuration in webpack.config.js we have to add on very beginning:
module.exports = { mode: 'development', devtool: 'inline-source-map',
If you have some troubles with webpack configuration with TypeScript in this tutorial, then check full code of webpack.config.js file on our gitlab here.
To be able to import in TypeScript .ts
files modules directly and not via import * as name ...
statement we must add the configuration to tsconfig.json
file :
{ "compilerOptions": { "esModuleInterop": true } }
Webpack Dev Server with TypeScript
At the end we must add very important and useful functionality – add a Webpack Dev Server.
In general, there is no matter if you use TypeScript or JavaScript for Webpack Dev Server. To use TypeScript the most important is its configuration done in steps descriebed above!
Webpack Dev Server is a Webpack plugin which track all pointed .ts, .js or .css files for changes (saves) and builds our application in the background in runtime and automatically refresh the browser page with our app.
Let’s install that via command:
npm install --save-dev webpack-dev-server
and in webpack.config.js file we must extend configuration by writing:
devServer: { contentBase: './dist' },
And of course, we must add a new npm
commad in our main package.json
configuration file: "dev": "webpack-dev-server --open"
and the whole “scripts” property will look like:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack --config webpack.config.js", "dev": "webpack-dev-server --open" },
Now when you write npm run dev
command in terminal it will open the new browser window on e.g. 8000 port http://localhost:8080
, your TypeScript or JavaScript code will be transpilled into ES5 syntax, and new index.html fill will be created in background with linkage to bundled TypeScript or JS and CSS files and whole app will start working in your browser. When you make some changes in TypeScript/JavaScript/CSS files, the Webpack Dev Server will detect it, and refresh your application in browser window just like that.
You can find all files of that project in our repository on gitlab here and each most important file is listed below:
Main Webpack configuration file: webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const path = require('path'); module.exports = { mode: 'development', devtool: 'inline-source-map', devServer: { contentBase: './dist' }, entry: './src/app.ss', //entry: './src/appTS.ts', output: { path: path.resolve(__dirname, './dist'), filename: 'bundle.js' }, plugins: [ new HtmlWebpackPlugin({ title: 'Optional chaining - test' }), new CleanWebpackPlugin() ], module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.ts$/, //include: /\appBabel.ts$/, //remove or comment to work with optional chaining in TS exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-typescript'], plugins: ["@babel/plugin-proposal-optional-chaining"] } } }, // TS-LOADER // { // test: /\.ts$/, // //include: /\appTsLoader.ts$/, //remove or comment to work with optional chaining in TS // exclude: /node_modules/, // use: 'ts-loader', // exclude: /node_modules/ // } ] } }
TypeScript configuration file: tsconfig.json
{ "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "module": "es6", "target": "es5", "target": "esnext", "jsx": "react", "allowJs": true }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ] }
Project configuration file: package.json
{ "name": "pure-webpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack --config webpack.config.js", "dev": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.8.3", "@babel/plugin-proposal-optional-chaining": "^7.8.3", "@babel/preset-env": "^7.8.3", "@babel/preset-typescript": "^7.8.3", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", "html-webpack-plugin": "^3.2.0", "ts-loader": "^6.2.1", "typescript": "^3.7.4", "webpack": "^4.41.5", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.10.1" } }
Main app file in JavaScript: app.js – used before TypeScript was initialized
const house = { room: { desk: ['pencil', 'computer'] } } const x = house.room.desk; const y = house.room?.desk; const z = house.toilet?.closet; document.querySelector('body').innerHTML += ` <style> .text { width: 500px; margin: 0 auto; background: #ccc; } </style> <div class="text"> <h2>BABEL PRESET-ENV</h2> Room Normal: ${x} <br> Room Chaining: ${y} <br> Toilet Chaining: ${z} </div> `;
Main app file in TypeScript: appTS.ts – used after TypeScript init
type Space = { desk?: string[], table?: string[], walls?: string[] }; type Building = { room?: Space, toilet?: Space, Kitchen?: Space }; const house: Building = { room: { desk: ['pencil', 'computer'] } } const x = house.room.desk; const y = house.room?.desk; const z = house.toilet?.table; export const run = () => { document.querySelector('body').innerHTML += ` <style> .text { width: 500px; margin: 0 auto; background: #ccc; } </style> <div class="text"> <h2>BABEL TYPESCRIPT</h2> Room Normal: ${x} <br> Room Chaining: ${y} <br> Toilet Chaining: ${z} </div> `; }; run();
This is everything in this tutorial about Webpack config with TypeScript and dev server!
I hope that now you will be able to create your webpack bundling to your own custom TypeScript or JavaScritp project. But rememebr that this knowledge is useful not only in own custom made TS/JS projects, but almost all modern JavaScript frameworks development environments like Vue CLI, NuxtJS or React-create-app are using webpack under the hood, so whenever you will need to customize how this environment works, than this knowledge will be priceless.