Skip to content

Conversation

@agibson-godaddy
Copy link
Contributor

@agibson-godaddy agibson-godaddy commented Feb 28, 2025

Issue: https://godaddy-corp.atlassian.net/browse/MWC-17961

Summary

This PR converts the codebase to ESM. The changes/impacts are:

Require => import

Instead of const { spawn } = require('child_process') we'd use this syntax: import { spawn } from 'node:child_process'

So moving from require to import

Exports

module.exports is no longer a thing, so all instances of that have been changed.

Task registration

Although not strictly part of the ESM migration, I also updated our deprecated usage of the gulp task() function. See https://gulpjs.com/docs/en/api/task

Previously we registered a task using task('my-task-name', functionNameHere . This is deprecated and the new way to do it is to simply export the function -- e.g. export function functionNameHere

One challenge was how to get our old task names back. Previously we could just specify the task name as a string, which allowed us to use characters like: copy:build . But we don't have that same flexibility when exporting a function directly, since a function can't contain some special characters. The way around that is to set the displayName on the thing we're exporting. Example:

const validateReadmeHeaders = (done) => {
    // do things
    done()
}

validateReadmeHeaders.displayName = 'validate:readme_headers'

export {
  validateReadmeHeaders
}

Without the change, we'd have to run sake validateReadmeHeaders, but by setting the display name, sake validate:readme_headers still works!

Task Updates

I tested each of these as I went.

  • build.js
  • bump.js
  • bundle.js
  • clean.js
  • compile.js
  • config.js
  • copy.js
  • defacceinate.js
  • deploy.js
  • github.js
  • imagemin.js
  • lint.js
  • makepot.js
  • prerelease.js
  • prompt.js
  • scripts.js
  • shell.js
  • styles.js
  • upfw.js
  • validate.js
  • watch.js
  • wc.js
  • zip.js

QA

Build / zip

  1. Run sake zip using a plugin that includes blocks (like a payment gateway)
    • Command runs successfully
  2. Upload zip to a test site and activate the plugin
    • Plugin loads, no fatal errors
  3. Visit checkout page using block checkout
    • The gateway's assets / block load correctly

Deployment

To be completed only once the above has been done successfully!

  1. Deploy any plugin using sake deploy
  2. Deploy a payment gateway or blocks plugin using sake deploy
  3. Deploy any plugin to WordPress dot org using sake deploy

After merge

@agibson-godaddy agibson-godaddy self-assigned this Feb 28, 2025
gulp.registry(ForwardReference())

// define default config
let defaults = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config was moved to new, separate file lib/config.js so that it can now be imported where required, instead of passing it around function parameters.

@agibson-godaddy
Copy link
Contributor Author

agibson-godaddy commented May 7, 2025

Note this PR is affected by this: gulpjs/gulp-cli#269

As such, it only works on Node <= v22.11.0

@agibson-godaddy agibson-godaddy changed the title [WIP] Convert code base to ESM Convert code base to ESM Aug 22, 2025
@agibson-godaddy agibson-godaddy marked this pull request as ready for review August 22, 2025 11:12

This comment was marked as outdated.

agibson-godaddy and others added 2 commits August 22, 2025 12:32
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR converts the entire codebase from CommonJS to ES Module (ESM) format, updating import/export syntax, task registration patterns, and modernizing several dependencies. The changes enable the project to use modern JavaScript module syntax while maintaining all existing functionality.

  • Convert all require() statements to import statements with proper Node.js module prefixes
  • Replace module.exports with named export statements throughout the codebase
  • Update Gulp task registration from deprecated gulp.task() to direct function exports with displayName properties

Reviewed Changes

Copilot reviewed 29 out of 31 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
package.json Add "type": "module" and update dependencies to ESM-compatible versions
gulpfile.js Convert to ESM, remove config loading logic, add task exports from all task modules
lib/sake.js Convert to ESM, move config logic to separate module, update import syntax
lib/config.js New module extracted from gulpfile.js containing configuration loading logic
tasks/*.js Convert all task files to ESM with named exports and updated import statements
pipes/*.js Convert pipe utility modules to ESM with named exports

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

// Compile all plugin assets
const compile = (done) => {
// default compile tasks
let tasks = [lintPhpTask, 'scripts', stylesTask, minifyImagesTask] // NOTE: do not import the `scripts` constant here, otherwise it creates a circular dependency
Copy link

Copilot AI Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded string 'scripts' in the tasks array creates an inconsistency with other imported tasks and may cause issues since task registration has changed to function exports. Consider resolving the circular dependency or using a different approach.

Suggested change
let tasks = [lintPhpTask, 'scripts', stylesTask, minifyImagesTask] // NOTE: do not import the `scripts` constant here, otherwise it creates a circular dependency
let tasks = [lintPhpTask, compileScripts, stylesTask, minifyImagesTask] // Use function reference instead of string for scripts task

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested inclusion of compileScripts is wrong. It's actually referencing the scriptsTask from scripts.js.

The reason we can't import it is because compile.js would be importing a function from scripts.js. But scripts.js already imports from compile.js and referencing that again would trigger compile.js to re-import from scripts.js -- hence kind of an infinite loop there.

I ran it by Claude and Claude agreed current implementation is best:

Prompt

In tasks/compile.js line 155 I note a circular dependency. The 'scripts' string references the scriptsTask defined in tasks/scripts.js . The problem was that if I import the scriptsTask function, it comes from a file that also then imports stuff from compile.js, which would then import from scripts.js again, thus a circular dependency.
How would you fix this, or is the way i have it now best?
Do not actually change the code, just describe a plan for me, or if what I have now is already the best/simplest option then say that.

Response

I can see the circular dependency issue clearly:

  1. tasks/compile.js line 3 imports compileScripts from tasks/scripts.js
  2. tasks/scripts.js line 3 imports compileScripts from tasks/compile.js
  3. tasks/compile.js line 155 uses the string 'scripts' instead of importing scriptsTask to avoid the circular dependency

Your current approach using the string 'scripts' is actually the best and simplest solution for this specific case. Here's why:

Why your current approach is optimal:

  • Gulp can resolve task names as strings at runtime, avoiding the circular import
  • It's the standard Gulp pattern for referencing tasks by name
  • Clean and simple - no architectural changes needed
  • Follows the principle of least change

Alternative approaches would be more complex:

  1. Dependency injection - Pass tasks as parameters, but adds complexity
  2. Shared module - Move common tasks to a separate file, but requires refactoring
  3. Lazy imports - Use dynamic imports, but adds async complexity
  4. Task registry - Create a central registry, but overengineers the solution

Your current solution is exactly what Gulp was designed for - referencing tasks by their registered names rather than direct function imports when circular dependencies would occur.

agibson-godaddy and others added 3 commits August 22, 2025 12:48
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@agibson-godaddy
Copy link
Contributor Author

Note this PR is affected by this: gulpjs/gulp-cli#269

As such, it only works on Node <= v22.11.0

That issue has now been fixed/merged. So this just needs testing with Node > 22.11.0 to confirm.

@nikolas4175-godaddy
Copy link
Contributor

nikolas4175-godaddy commented Jan 27, 2026

✅ Completed Build + Zip testing for block plugin.

Test env:

  • PHP 8.4.10
  • Node 22.20.0
  • WP 6.9
  • WC 10.4.3
  • Authorize.Net 3.10.14

QA

  • Run sake zip using a plugin that includes blocks
    • Command runs successfully
  • Upload zip to a test site and activate the plugin
    • Plugin loads, no fatal errors
  • Visit checkout page using block checkout
    • The gateway's assets / block load correctly
  • E2E: Place order for payment via gateway (test env)
    • Payment processes and data is correctly stored on the order

Copy link
Contributor

@nikolas4175-godaddy nikolas4175-godaddy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple Qs in the config, but other than that QA checks out and changes look good! 🚀

// TODO: allow passing in config file path or config as string (for multi-plugin repos?)
let localConfig = {}

// support supplying a single / parent config file in multi-plugin repos
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agibson-godaddy do we still need to support this after restructuring the Memberships add-on repos?

Copy link
Contributor Author

@agibson-godaddy agibson-godaddy Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can handle that for a separate discussion.

We no longer need it, but I think the greater question is who we're maintaining this for (just ourselves? others?) Do we care about removing that if people-who-are-not-us might be using it?

Regardless of what the answer is, I think we can make that a separate discussion and PR.

This change might also tie into the below comment about versioning Sake / major version release.

let configFilePath = path.join(process.cwd(), 'sake.config.js')

if (existsSync(configFilePath)) {
localConfig = _.merge(localConfig, require(configFilePath))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any interest in updating sake.config.js files to be modules as well? 🤔

If so I think we'll need to replace our require() usage in this file, but we can save that for a later enhancement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking let's do it later? I didn't want to make a change in sake that required sake.config.js files be updated as well. Might be better to do something like:

  1. Actually start versioning sake (because requiring a config update would be a major change)
  2. Update sake to expect sake.config.js files to be modules
  3. Tag as major version
  4. Then individual repos can update their configs & upgrade sake, or continue to use the old one for a while

tl;dr trying to avoid breaking changes

@agibson-godaddy agibson-godaddy merged commit c304d7f into master Jan 30, 2026
@agibson-godaddy agibson-godaddy deleted the mwc-17961 branch January 30, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants