Skip to content

Instantly share code, notes, and snippets.

@noraj
Last active May 14, 2024 17:07
Show Gist options
  • Save noraj/007a943dc781dc8dd3198a29205bae04 to your computer and use it in GitHub Desktop.
Save noraj/007a943dc781dc8dd3198a29205bae04 to your computer and use it in GitHub Desktop.
Moving gulpfile from CommonJS (CJS) to ECMAScript Modules (ESM)

Moving gulpfile from CommonJS (CJS) to ECMAScript Modules (ESM)

Context

del v7.0.0 moved to pure ESM (no dual support), which forced me to move my gulpfile to ESM to be able to continue to use del.

The author sindresorhus maintains a lot of npm packages and does not want to provides an upgrade guide for each package so he provided a generic guide. But this guide is a bit vague because it's generic and not helping for gulp, hence this guide.

Guide

  1. Rename gulpfile.js to gulpfile.mjs

The first step is easy, since v2.3.0 (#214) gulp-cli supports ESM. So we can just rename gulpfile.js to gulpfile.mjs.

This avoid having to add "type": "module" to package.json which makes no sense if you are not providing a package but rather just use gulp to automate deployment tasks for example.

  1. Change gulp import

Gulp doesn't support all module.exports as named exports yet (#2634) but CJS modules can always be imported via the default export:

-const { series, parallel, src, dest, task } = require('gulp');
+import gulp from 'gulp';
+const { series, parallel, src, dest, task } = gulp;
  1. Change gulp plugins import

Most gulp plugin can be changed easily, eg.

-const pug = require('gulp-pug');
+import pug from 'gulp-pug';
-const replace = require('gulp-replace');
+import replace from 'gulp-replace';
  1. Change gulp-sass

gulp-sass is a tricky one but hopefuly the project README explains how to do it.

-const sass = require('gulp-sass')(require('sass'));
+import dartSass from 'sass';
+import gulpSass from 'gulp-sass';
+const sass = gulpSass(dartSass);

Update: since sass 1.63.0 undocumented breaking change (sass/dart-sass#2011), an import syntax change is reaquired.

-import dartSass from 'sass';
+import * as dartSass from 'sass';
  1. Change del

I could have done import { deleteAsync as del } from 'del'; to keep this named del but it's advised to keep descriptive names to avoid namespace conflicts so I moved it to deleteAsync as recommended and renamed all call to del to deleteAsync in gulpfile.mjs.

-const del = require('del');
+import { deleteAsync } from 'del';
  1. Use node: URL scheme

It's recommanded to use node: URL scheme to import Node.js builtin modules so they are referenced by valid absolute URL strings.

-const { exec } = require('child_process');
+import { exec } from 'node:child_process';

Example

Example of migration for my project:

Lexicon

  • ESM = ES Modules = ECMAScript Modules
  • CSJ = CommonJS

Extensions

System Extension
ESM .mjs
CJS .cjs
Default module system .js

More

More about ESM in:

@vivatm
Copy link

vivatm commented Sep 5, 2022

Thank you for your guide, it's very usefull ! If I may, you could add a step to specify how to switch from CSJ exports to ESM (I saw in your examples that you used gulp.task instead of exports.taskname)

@Aex4Hunter
Copy link

Thank you so much, it worked. I could finally fix my problems!

@AveryLebene
Copy link

Thanks a lot.. this fixed my problem.

@VividVisions
Copy link

Thank you for your guide, it's very usefull ! If I may, you could add a step to specify how to switch from CSJ exports to ESM (I saw in your examples that you used gulp.task instead of exports.taskname)

You can just use named exports. For instance:

function css() {
   // Gulp task
}

export css;

or

function taskOne() {
   // Gulp task
}

const allTasks = parallel(/* Gulp tasks */);

export {
   taskOne as one,
   allTasks as all
}

@raif410
Copy link

raif410 commented Jul 13, 2023

Thanks a lot!!! 😀

@vHeemstra
Copy link

vHeemstra commented Jul 20, 2023

Thanks for the great guide!


Default task

Expanding on @VividVisions's reply, your default task (when running just command gulp) can be exported like this:

function taskDefault() {
  // ...
}

export default taskDefault;

Named and default tasks

Or, when combining named tasks and a default task:

const taskOne = function() {
  // ...
}

const taskTwo = function() {
  // ...
}

const taskDefault = function() {
  // ...
}

export {
	taskDefault as default,
	taskOne as one,
	taskTwo as two,
}

Or, if you prefer:

export const one = () => { /* ... */ }

export const two = () => { /* ... */ }

export default () => { /* ... */ }

@Giiert
Copy link

Giiert commented Dec 7, 2023

Thanks a lot!

@korinektomas
Copy link

You saved my evening! 🙏

@stefcab
Copy link

stefcab commented Mar 5, 2024

Thanks a lot !

using the node.js fs in place del, :

import fs from 'fs/promises';

async function clean() {
await fs.rm(destination, { recursive: true, force: true });
}`

@kirilvit
Copy link

If you encounter an error that the gulpfile can't be found after renaming gulpfile.js to gulpfile.mjs - make sure you are using "gulp-cli": "^2.3.0"

@arex388
Copy link

arex388 commented Apr 8, 2024

How would this work with Visual Studio's Task Runner Explorer? I'm using VS 2022 and after I followed the steps above, the TRE no longer finds the gulpfile.mjs.

@MiguelDebruyne
Copy link

Thank you so much! This helped me a lot to understand what to do. Thanks to @VividVisions as well since that information was critical.
However, parallel(), and series() have stop working now and I don't know why. They simply don't fire the functions they are given.

const watchSource = function (done) {
  series(startServer, watchFiles);
  done();
};

@noraj
Copy link
Author

noraj commented Apr 18, 2024

@MiguelDebruyne
Copy link

@noraj Thank you very much 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment