Gulp
This quick start guide will teach you how to build TypeScript withgulp and then addBrowserify,terser, orWatchify to the gulp pipeline.This guide also shows how to addBabel functionality usingBabelify.
We assume that you’re already usingNode.js withnpm.
Minimal project
Let’s start out with a new directory.We’ll name itproj
for now, but you can change it to whatever you want.
shell
mkdir projcd proj
To start, we’re going to structure our project in the following way:
proj/├─ src/└─ dist/
TypeScript files will start out in yoursrc
folder, run through the TypeScript compiler and end up indist
.
Let’s scaffold this out:
shell
mkdir srcmkdir dist
Initialize the project
Now we’ll turn this folder into an npm package.
shell
npm init
You’ll be given a series of prompts.You can use the defaults except for your entry point.For your entry point, use./dist/main.js
.You can always go back and change these in thepackage.json
file that’s been generated for you.
Install our dependencies
Now we can usenpm install
to install packages.First installgulp-cli
globally (if you use a Unix system, you may need to prefix thenpm install
commands in this guide withsudo
).
shell
npm install -g gulp-cli
Then installtypescript
,gulp
andgulp-typescript
in your project’s dev dependencies.Gulp-typescript is a gulp plugin for TypeScript.
shell
npm install --save-dev typescript gulp@4.0.0 gulp-typescript
Write a simple example
Let’s write a Hello World program.Insrc
, create the filemain.ts
:
ts
functionhello(compiler:string) {console.log(`Hello from${compiler}`);}hello("TypeScript");
In the project root,proj
, create the filetsconfig.json
:
{" ": ["src/main.ts"]," ": {" ":true," ":"es5"}}
Create agulpfile.js
In the project root, create the filegulpfile.js
:
js
vargulp =require("gulp");varts =require("gulp-typescript");vartsProject =ts.createProject("tsconfig.json");gulp.task("default",function () {returntsProject.src().pipe(tsProject()).js.pipe(gulp.dest("dist"));});
Test the resulting app
shell
gulpnode dist/main.js
The program should print “Hello from TypeScript!“.
Add modules to the code
Before we get to Browserify, let’s build our code out and add modules to the mix.This is the structure you’re more likely to use for a real app.
Create a file calledsrc/greet.ts
:
ts
exportfunctionsayHello(name:string) {return`Hello from${name}`;}
Now change the code insrc/main.ts
to importsayHello
fromgreet.ts
:
ts
import {sayHello }from"./greet";console.log(sayHello("TypeScript"));
Finally, addsrc/greet.ts
totsconfig.json
:
{" ": ["src/main.ts","src/greet.ts"]," ": {" ":true," ":"es5"}}
Make sure that the modules work by runninggulp
and then testing in Node:
shell
gulpnode dist/main.js
Notice that even though we used ES2015 module syntax, TypeScript emitted CommonJS modules that Node uses.We’ll stick with CommonJS for this tutorial, but you could setmodule
in the options object to change this.
Browserify
Now let’s move this project from Node to the browser.To do this, we’d like to bundle all our modules into one JavaScript file.Fortunately, that’s exactly what Browserify does.Even better, it lets us use the CommonJS module system used by Node, which is the default TypeScript emit.That means our TypeScript and Node setup will transfer to the browser basically unchanged.
First, install browserify,tsify, and vinyl-source-stream.tsify is a Browserify plugin that, like gulp-typescript, gives access to the TypeScript compiler.vinyl-source-stream lets us adapt the file output of Browserify back into a format that gulp understands calledvinyl.
shell
npm install --save-dev browserify tsify vinyl-source-stream
Create a page
Create a file insrc
namedindex.html
:
html
<!DOCTYPEhtml><html><head><metacharset="UTF-8"/><title>Hello World!</title></head><body><pid="greeting">Loading ...</p><scriptsrc="bundle.js"></script></body></html>
Now changemain.ts
to update the page:
ts
import {sayHello }from"./greet";functionshowHello(divName:string,name:string) {constelt =document.getElementById(divName);elt.innerText =sayHello(name);}showHello("greeting","TypeScript");
CallingshowHello
callssayHello
to change the paragraph’s text.Now change your gulpfile to the following:
js
vargulp =require("gulp");varbrowserify =require("browserify");varsource =require("vinyl-source-stream");vartsify =require("tsify");varpaths = {pages: ["src/*.html"],};gulp.task("copy-html",function () {returngulp.src(paths.pages).pipe(gulp.dest("dist"));});gulp.task("default",gulp.series(gulp.parallel("copy-html"),function () {returnbrowserify({basedir:".",debug:true,entries: ["src/main.ts"],cache: {},packageCache: {},}).plugin(tsify).bundle().pipe(source("bundle.js")).pipe(gulp.dest("dist"));}));
This adds thecopy-html
task and adds it as a dependency ofdefault
.That means any timedefault
is run,copy-html
has to run first.We’ve also changeddefault
to call Browserify with the tsify plugin instead of gulp-typescript.Conveniently, they both allow us to pass the same options object to the TypeScript compiler.
After callingbundle
we usesource
(our alias for vinyl-source-stream) to name our output bundlebundle.js
.
Test the page by running gulp and then openingdist/index.html
in a browser.You should see “Hello from TypeScript” on the page.
Notice that we specifieddebug: true
to Browserify.This causes tsify to emit source maps inside the bundled JavaScript file.Source maps let you debug your original TypeScript code in the browser instead of the bundled JavaScript.You can test that source maps are working by opening the debugger for your browser and putting a breakpoint insidemain.ts
.When you refresh the page the breakpoint should pause the page and let you debuggreet.ts
.
Watchify, Babel, and Terser
Now that we are bundling our code with Browserify and tsify, we can add various features to our build with browserify plugins.
Watchify starts gulp and keeps it running, incrementally compiling whenever you save a file.This lets you keep an edit-save-refresh cycle going in the browser.
Babel is a hugely flexible compiler that converts ES2015 and beyond into ES5 and ES3.This lets you add extensive and customized transformations that TypeScript doesn’t support.
Terser compacts your code so that it takes less time to download.
Watchify
We’ll start with Watchify to provide background compilation:
shell
npm install --save-dev watchify fancy-log
Now change your gulpfile to the following:
js
vargulp =require("gulp");varbrowserify =require("browserify");varsource =require("vinyl-source-stream");varwatchify =require("watchify");vartsify =require("tsify");varfancy_log =require("fancy-log");varpaths = {pages: ["src/*.html"],};varwatchedBrowserify =watchify(browserify({basedir:".",debug:true,entries: ["src/main.ts"],cache: {},packageCache: {},}).plugin(tsify));gulp.task("copy-html",function () {returngulp.src(paths.pages).pipe(gulp.dest("dist"));});functionbundle() {returnwatchedBrowserify.bundle().on("error",fancy_log).pipe(source("bundle.js")).pipe(gulp.dest("dist"));}gulp.task("default",gulp.series(gulp.parallel("copy-html"),bundle));watchedBrowserify.on("update",bundle);watchedBrowserify.on("log",fancy_log);
There are basically three changes here, but they require you to refactor your code a bit.
- We wrapped our
browserify
instance in a call towatchify
, and then held on to the result. - We called
watchedBrowserify.on('update', bundle);
so that Browserify will run thebundle
function every time one of your TypeScript files changes. - We called
watchedBrowserify.on('log', fancy_log);
to log to the console.
Together (1) and (2) mean that we have to move our call tobrowserify
out of thedefault
task.And we have to give the function fordefault
a name since both Watchify and Gulp need to call it.Adding logging with (3) is optional but very useful for debugging your setup.
Now when you run Gulp, it should start and stay running.Try changing the code forshowHello
inmain.ts
and saving it.You should see output that looks like this:
shell
proj$ gulp[10:34:20] Using gulpfile ~/src/proj/gulpfile.js[10:34:20] Starting'copy-html'...[10:34:20] Finished'copy-html' after 26 ms[10:34:20] Starting'default'...[10:34:21] 2824 bytes written (0.13 seconds)[10:34:21] Finished'default' after 1.36 s[10:35:22] 2261 bytes written (0.02 seconds)[10:35:24] 2808 bytes written (0.05 seconds)
Terser
First install Terser.Since the point of Terser is to mangle your code, we also need to install vinyl-buffer and gulp-sourcemaps to keep sourcemaps working.
shell
npm install --save-dev gulp-terser vinyl-buffer gulp-sourcemaps
Now change your gulpfile to the following:
js
vargulp =require("gulp");varbrowserify =require("browserify");varsource =require("vinyl-source-stream");varterser =require("gulp-terser");vartsify =require("tsify");varsourcemaps =require("gulp-sourcemaps");varbuffer =require("vinyl-buffer");varpaths = {pages: ["src/*.html"],};gulp.task("copy-html",function () {returngulp.src(paths.pages).pipe(gulp.dest("dist"));});gulp.task("default",gulp.series(gulp.parallel("copy-html"),function () {returnbrowserify({basedir:".",debug:true,entries: ["src/main.ts"],cache: {},packageCache: {},}).plugin(tsify).bundle().pipe(source("bundle.js")).pipe(buffer()).pipe(sourcemaps.init({loadMaps:true })).pipe(terser()).pipe(sourcemaps.write("./")).pipe(gulp.dest("dist"));}));
Notice thatterser
itself has just one call — the calls tobuffer
andsourcemaps
exist to make sure sourcemaps keep working.These calls give us a separate sourcemap file instead of using inline sourcemaps like before.Now you can run Gulp and check thatbundle.js
does get minified into an unreadable mess:
shell
gulpcat dist/bundle.js
Babel
First install Babelify and the Babel preset for ES2015.Like Terser, Babelify mangles code, so we’ll need vinyl-buffer and gulp-sourcemaps.By default Babelify will only process files with extensions of.js
,.es
,.es6
and.jsx
so we need to add the.ts
extension as an option to Babelify.
shell
npm install --save-dev babelify@8 babel-core babel-preset-es2015 vinyl-buffer gulp-sourcemaps
Now change your gulpfile to the following:
js
vargulp =require("gulp");varbrowserify =require("browserify");varsource =require("vinyl-source-stream");vartsify =require("tsify");varsourcemaps =require("gulp-sourcemaps");varbuffer =require("vinyl-buffer");varpaths = {pages: ["src/*.html"],};gulp.task("copy-html",function () {returngulp.src(paths.pages).pipe(gulp.dest("dist"));});gulp.task("default",gulp.series(gulp.parallel("copy-html"),function () {returnbrowserify({basedir:".",debug:true,entries: ["src/main.ts"],cache: {},packageCache: {},}).plugin(tsify).transform("babelify", {presets: ["es2015"],extensions: [".ts"],}).bundle().pipe(source("bundle.js")).pipe(buffer()).pipe(sourcemaps.init({loadMaps:true })).pipe(sourcemaps.write("./")).pipe(gulp.dest("dist"));}));
We also need to have TypeScript target ES2015.Babel will then produce ES5 from the ES2015 code that TypeScript emits.Let’s modifytsconfig.json
:
{" ": ["src/main.ts"]," ": {" ":true," ":"es2015"}}
Babel’s ES5 output should be very similar to TypeScript’s output for such a simple script.
The TypeScript docs are an open source project. Help us improve these pagesby sending a Pull Request ❤
Last updated: Oct 06, 2025