JavaScript Build Process for Cross-Platform Development

I’ve been working on a build setup that lets me write JavaScript code that works in both browsers and Node.js environments. I’m wondering if others have tried something similar or have ideas to make it better.

My Current Setup

I use Node.js tooling for my frontend development. My build process uses Gulp with file watching to combine and compress all source files into one minified file that goes into the dist/scripts directory.

For unit testing, I use Mocha and need a main entry file that imports all my source modules. To make the same code work in both environments, I put everything under a global namespace like:

appCore.utilities.dataHandler

In my main app.js file, I export to Node when the module system is available:

if (typeof module !== 'undefined' && module.exports) {
    module.exports = appCore;
}

This lets me write my modules in a simple way:

(function() { 
    appCore.utilities.dataHandler = function() {
        // implementation here
    };
})();

Or even just:

appCore.utilities.dataHandler = function() {
    // code goes here
};

The Problem

This approach works great in the browser since everything loads together, but in Node.js I sometimes get errors when one module tries to use another because there’s no proper dependency management.

I’m thinking about adding standard Node.js require statements to each file, then having my build tool remove those during the browser build process.

Has anyone dealt with this kind of cross-platform JavaScript setup before? What approaches worked best for you?

hmm, what about rollup? handles esm modules great and outputs multiple formats. why gulp over modern bundlers? also - sticking with namespaces or thinking about switching to proper modules?

just use webpack with separate configs for browser and node builds. way cleaner than manually removing require statements. set target: ‘node’ for server, target: ‘web’ for browser. handles all the module stuff automatically without weird global hacks.

I encountered similar challenges while developing a cross-platform library. I recommend moving away from global namespaces and using the UMD (Universal Module Definition) pattern. This approach automatically detects the environment and manages exports accordingly. By wrapping each module with UMD, you can address the issues with dependency loading in Node.js, as it accurately identifies the module system in use. For the build process, employing Webpack is effective, allowing separate entry points for both the browser and Node.js. This way, the browser version can bundle everything, while the Node.js version maintains individual modules with the proper require statements, streamlining the approach significantly.