Search code examples
javascriptreactjsgulpbrowserify

Uncaught Error: Cannot find module in Browserify Gulp when transpiled


I've been desperately trying to sort out horrifically slow builds due to bundled dependencies in my JS with Browserify. To that effect I've come up with some utility methods that my gulp files call, presented below...

const paths = require("./paths.gulpfile")
const gulp = require("gulp")
const uglify = require("gulp-uglify")
const sourcemaps = require("gulp-sourcemaps")
const browserify = require("browserify")
const shim = require("browserify-shim")
const watchify = require("watchify")
const babelify = require("babelify")
const source = require("vinyl-source-stream")
const buffer = require("vinyl-buffer")
const gutil = require("gulp-util")
const babel = require("gulp-babel")

const externalLibs = [
    "jquery", 
    "react",
    "react-dom",    
    "prop-types",
    "bootstrap-sass" 
]
const ignoredLibs = []
const vendorLibs = [
    "google-maps",
    "history", "history/createBrowserHistory",
    "lodash", "lodash/debounce",
    "path-to-regexp"
]

/**
 * JS related gulp tasks
 * 
 * @returns {object} utility functions for JS based gulp tasks.
 */
module.exports = function(compiledName) {

    /**
     * Bundle definition for dev.
     * 
     * @param {object} bundler 
     * @returns A bundle.
     */
    const appBundle = function(bundler) {
        return bundler.bundle()
            .pipe(source(compiledName + ".js"))
            .pipe(buffer())
            .pipe(sourcemaps.init({ loadMaps: true }))
            .pipe(babel())
            .pipe(sourcemaps.write("./"))
            .pipe(gulp.dest(paths.scriptsBuildPath))
    }

    /**
     * Bundle definition for production.
     * 
     * @param {object} bundler 
     * @returns A bundle.
     */
    const appMinBundle = function(bundler) {
        return bundler.bundle()
            .pipe(source(compiledName + ".min.js"))
            .pipe(buffer())
            .pipe(sourcemaps.init({ loadMaps: true }))
            .pipe(babel())
            .pipe(uglify({
                compress: true,
                mangle: {
                    except: [
                        "$",
                        "createBrowserHistory", 
                        "debounce",
                        "GoogleMapsLoader", 
                        "history", 
                        "jQuery",
                        "lodash",
                        "PropTypes",
                        "React", 
                        "ReactDom",
                        "toRegex"
                    ]
                }
            }))
                .on("error", gutil.log)
            .pipe(sourcemaps.write("./"))
            .pipe(gulp.dest(paths.scriptsBuildPath))
    }

    /**
     * Bundle definition for vendors.
     * 
     * @param {object} bundler The browserify instance
     * @param {string} env The environment
     * @returns A bundle.
     */
    const vendorsBundle = function(bundler, env) {
        // eslint-disable-next-line
        process.env.NODE_ENV = env ? env : process.env.NODE_ENV
        return bundler.bundle()
            .pipe(source("vendors.js"))
            .pipe(buffer())
            .pipe(sourcemaps.init({ loadMaps: true }))
            .pipe(sourcemaps.write("./"))
            .pipe(gulp.dest(paths.scriptsBuildPath))
    }

    /**
     * Minified Bundle definition for vendors.
     * 
     * @param {object} bundler The browserify instance
     * @param {string} env The environment
     * @returns A bundle.
     */
    const vendorsMinBundle = function(bundler, env) {
        // eslint-disable-next-line
        process.env.NODE_ENV = env ? env : process.env.NODE_ENV
        return bundler
            .transform("uglifyify", { global: true })
            .bundle()
            .pipe(source("vendors.min.js"))
            .pipe(buffer())
            .pipe(sourcemaps.init({ loadMaps: true }))
            .pipe(uglify({
                compress: true,
                mangle: true
            }))
            .pipe(sourcemaps.write("./"))
            .pipe(gulp.dest(paths.scriptsBuildPath))
    }

    /**
     * Builds the JavaScript bundles.
     * 
     * @param {bool} debugMode 
     * @param {bool} watch 
     * @param {func} bundleCallback 
     * @returns A bundle.
     */
    const buildJs = function(debugMode, watch, bundleCallback) {
        const entryPoint = paths.scriptsSrcPath + "app.js"

        let bundler = browserify({
            entries: [entryPoint],
            transform: [[babelify, { "presets": ["react", "es2015", "stage-0"] }], shim],
            debug: debugMode,
            cache: {}, packageCache: {}, fullPaths: true
        })

        bundler.external(externalLibs)
        bundler.external(vendorLibs)

        ignoredLibs.forEach(function(lib) {
            bundler.ignore(require.resolve(lib, { expose: lib }))
        })

        if (watch) {
            bundler = watchify(bundler)

            bundler.on("update", function() {
                bundleCallback(bundler)
            })

            bundler.on("log", function(msg) {
                gutil.log(msg)
            })
        }

        return bundleCallback(bundler)
    }

    /**
     * Builds the JavaScript vendor bundle.
     * 
     * @param {string} env The environment
     * @returns A bundle.
     */
    const buildVendorJs = function(env) {

        const bundler = browserify({
            debug: true
        })

        //bundler.external(externalLibs)

        vendorLibs.forEach(function(lib) {
            bundler.require(lib)
        })

        const bigBundle = vendorsBundle(bundler, env)
        return env == "production" ? vendorsMinBundle(bundler, env) : bigBundle
    }

    return {
        buildJs : buildJs,
        buildVendorJs: buildVendorJs,
        appBundle: appBundle,
        appMinBundle: appMinBundle
    }
}

The externalLibs are supposed to be CDN loaded and are excluded entirely, the vendorLibs are to be thrown together into a single minified file and the appBundle is self explanatory.

I had this working prior to introducing a separate vendor.js file and I had all the dependencies in there but I kept having problems getting React to play ball when minified and eventually just gave up and when back to the CDN (Not even prepared to go there again - far too many wasted days).

This leaves my remaining vendors but I'm now getting an issue where PropTypes is not being found....

_prelude.js:1 Uncaught Error: Cannot find module 'prop-types' at s (_prelude.js:1)

This followed similar errors trying to access React when prop-types was part of the vendor.js package - I'm out of ideas what to try next.


Solution

  • I found the solution was that I had not added the new externals to bowserify shim config in package.json...

      "browserify-shim": {
        "./node_modules/jquery/dist/jquery.js": "$",
        "react": "global:React",
        "react-dom": "global:ReactDOM",
        "prop-types": "global:PropTypes"