Gulp 4 tasks won’t finish when using @rollup/stream and es6 target in tsconfig.json

My application relies on an internal company TS library that sees infrequent edits. I’ve just updated it and now one of the gulp tasks used to build it will fail. All other tasks will work in isolation or used in sequence/parallel but the one that uses the @rollup/stream package causes gulp to hang after declaring itself finished. This was discovered as the build automation eventually killed the build as it had paused for 2 hours waiting for the gulp tasks to complete.

I had to update our internal library as it was failing the build due to the following issue.

'rollup' errored after 1.51 s
Error: '__spreadArray' is not exported by node_modulestslibtslib.es6.js, imported by srcObjectUtils.ts

So I updated the tsconfig.json file from "target": "es5", to "target": "es6", which causes the hang with rollup.

Running gulp rollup gives the following output

Using gulpfile path/to/gulp/file
Starting 'rollup'...
//
// Outputs all the files being rolled up here...
//
Finished 'rollup' after 1.43 s
// The gulp process then stays open instead of closing and returning me to my prompt

As an example I ran the task locally and later exited it myself with control+c and it reported waiting there for 10m15s

enter image description here

Having read all the docs and SO questions I could find I’m sure we’re doing as the async gulp tasks docs suggest for handling streams yet it won’t work. I’ve then tried manually closing the returned rollup stream myself using .on('end', () => { //etc }), wrapping things in promises and returning a promise that resolves when the stream is finished, using the callback provided to each task function, combinations of the above and yet I can’t get the rollup task to properly finish and return control back to the prompt.

I’ve included the relevant parts of my gulpfile, the tsconfig.json and my package.json dependencies.

// gulpfile.js

const gulp = require('gulp');
const gulpif = require('gulp-if');
const terser = require("gulp-terser");
const typescript = require("@rollup/plugin-typescript");
const rollup = require("@rollup/stream");
const buffer = require('vinyl-buffer');
const source = require('vinyl-source-stream');

const production = process.env.NODE_ENV == 'production';

function rollupTask () {
    // rollup() returns a stream, so returning this should satisfy the async tasks requirements. 
    // When the stream closes it should tell gulp the tasks has finished right?
    return rollup({
        input: "./src/main.ts",
        external: [
            "lodash",
        ],
        output: {
            format: "cjs",
            sourcemap: true
        },
        plugins: [
            typescript(),
        ],
    })
    .pipe(source("common.bundle.js"))
    .pipe(buffer())
    .pipe(gulpif(production, terser()))
    .pipe(gulp.dest('./dist'));
};

exports.rollup = rollupTask;
// tsconfig.json
{
    "compilerOptions": {
        "declaration": false,
        "module": "es2015",
        "target": "es6",
        "emitDecoratorMetadata": true,
        "listFiles": true,
        "noImplicitAny": false,
        "noUnusedLocals": true,
        "noLib": false,
        "removeComments": true,
        "sourceMap": true,
        "experimentalDecorators": true,
        "lib": [
            "ES5",
            "Dom",
            "ScriptHost",
            "ES2015",
            "ES2016",
            "ES2017"
        ]
    }
}
// package.json dependencies
 "dependencies": {
    "lodash": "^4.17.5"
  },
  "devDependencies": {
    "@rollup/plugin-typescript": "^6.1.0",
    "@rollup/stream": "^1.1.0",
    "@types/lodash": "^4.14.165",
    "del": "^6.0.0",
    "gulp": "^4.0.2",
    "gulp-if": "^3.0.0",
    "gulp-sourcemaps": "^3.0.0",
    "gulp-terser": "^2.0.0",
    "gulp-tslint": "^8.0.0",
    "gulp-typescript": "^5.0.1",
    "merge2": "^1.4.1",
    "rollup": "^2.33.3",
    "tslint": "^6.1.3",
    "typescript": "^4.0.5",
    "vinyl-buffer": "^1.0.1",
    "vinyl-source-stream": "^2.0.0"
  },

If it turns out I can keep using "target": "es5", in the tsconfig.json and a fix for the __spreadArray issue is found then I can do that as well.

Answer

I was stuck between 2 different errors:

  • In tsconfig.json setting "target" : "ES6" would break by never quitting the gulp task.
  • In tsconfig.json setting to "target" : "ES5" would complain about a missing __spreadArray function in tslib.

Here’s how I ended up fixing this:

  1. Updating all the dev dependencies to latest versions.
  2. Manually tslib using npm i --save-dev tslib to ensure there was a latest version available. Because I updated the TS version in step 1. which should include the latest tslib this is likely unnecessary…but it felt good to include it manually anyway.
  3. I found a suggestion to use a fork of @rollup/plugin-typescript named rollup-plugin-typescript2 to make it work. This itself is a strange issue as supposedly that fork only exists to add error logging in the console to tell you about any TS errors. But now the original project also outputs errors and yet doesn’t work with gulp for us (or maybe just me) anymore. The fork is maintained and popular on npm so I’m not too concerned but I’d still prefer to to be on the “official” package. Obviously I changed my gulpfile.js to use this new package instead but it’s a drop in replacement as far as API.
  4. Also added the paths value to tsconfig.json to point to the installed tslib node module which I manually checked to make sure it had the __spreadArray function we needed. I found this fix in this answer while I was related issues for clues

Some combination of all the above fixed it. Likely 3 and 4 were the main fixes.

The following is the updated versions of my tsconfig.json and the relevant part of my package.json in case it’s useful to any future reader.

// tsconfig.json
{
    "compilerOptions": {
        "declaration": false,
        "module": "ES2015",
        "target": "ES6",
        "emitDecoratorMetadata": true,
        "listFiles": true,
        "noImplicitAny": false,
        "noUnusedLocals": true,
        "noLib": false,
        "removeComments": true,
        "sourceMap": true,
        "experimentalDecorators": true,
        "importHelpers": true,
        "paths": {
            "tslib": [
                "./node_modules/tslib/tslib.d.ts"
            ]
        },
        "lib": [
            "ES5",
            "Dom",
            "ScriptHost",
            "ES2015",
            "ES2016",
            "ES2017"
        ]
    }
}
// package.json
"dependencies": {
    "lodash": "^4.17.21"
  },
  "devDependencies": {
    "@rollup/plugin-typescript": "^8.2.5",
    "@rollup/stream": "^2.0.0",
    "@types/lodash": "^4.14.165",
    "del": "^6.0.0",
    "gulp": "^4.0.2",
    "gulp-if": "^3.0.0",
    "gulp-sourcemaps": "^3.0.0",
    "gulp-terser": "^2.0.1",
    "gulp-tslint": "^8.1.4",
    "gulp-typescript": "^5.0.1",
    "merge2": "^1.4.1",
    "rollup": "^2.56.3",
    "rollup-plugin-typescript2": "^0.30.0",
    "tslib": "^2.3.1",
    "tslint": "^6.1.3",
    "typescript": "^4.4.2",
    "vinyl-buffer": "^1.0.1",
    "vinyl-source-stream": "^2.0.0"
  },
  "files": [
    "dist/"
  ]