Exporting a function to the front-end with Webpack

I’m new to webpack and trying to set up a basic implementation.

I have a file script.js with some functions in it

scripts.js

export foo = () => {
   console.log('foo')
}

export bar = () => {
   console.log('bar')
}

And I’m trying to add it to my webpack bundle.js such that I can use these functions in the markup.

webpack.confi.js

const path = require('path')

module.exports = {
    entry: './index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
}

My index.js looks like this:

import * as scripts from './scripts.js';

This works in node because if I add console.log(scripts.foo()) to my index.js below the import line, I can observe the console log.

But if I try to call foo() in my markup, I get an undefined error:

Uncaught ReferenceError: foo is not defined

index.html

<script src="./dist/bundle.js"></script>
<script>

    scripts.foo(); // throws undefined error

</script>

What am I missing?

1 thought on “Exporting a function to the front-end with Webpack”

  1. The whole of Webpack’s bundled output is inside an IIFE. For example, a script with only two modules, one importing from the other:

    export default 'someVar';
    
    import someVar from './someVar';
    console.log(someVar);
    

    produces the following output:

    /******/ (function(modules) { // webpackBootstrap
    /******/    // The module cache
    /******/    var installedModules = {};
    /******/
    /******/    // The require function
    /******/    function __webpack_require__(moduleId) {
    /******/
    /******/        // Check if module is in cache
    /******/        if(installedModules[moduleId]) {
    /******/            return installedModules[moduleId].exports;
    /******/        }
    /******/        // Create a new module (and put it into the cache)
    /******/        var module = installedModules[moduleId] = {
    /******/            i: moduleId,
    /******/            l: false,
    /******/            exports: {}
    /******/        };
    /******/
    /******/        // Execute the module function
    /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/        // Flag the module as loaded
    /******/        module.l = true;
    /******/
    /******/        // Return the exports of the module
    /******/        return module.exports;
    /******/    }
    /******/
    /******/
    /******/    // expose the modules object (__webpack_modules__)
    /******/    __webpack_require__.m = modules;
    /******/
    /******/    // expose the module cache
    /******/    __webpack_require__.c = installedModules;
    /******/
    /******/    // define getter function for harmony exports
    /******/    __webpack_require__.d = function(exports, name, getter) {
    /******/        if(!__webpack_require__.o(exports, name)) {
    /******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
    /******/        }
    /******/    };
    /******/
    /******/    // define __esModule on exports
    /******/    __webpack_require__.r = function(exports) {
    /******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    /******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    /******/        }
    /******/        Object.defineProperty(exports, '__esModule', { value: true });
    /******/    };
    /******/
    /******/    // create a fake namespace object
    /******/    // mode & 1: value is a module id, require it
    /******/    // mode & 2: merge all properties of value into the ns
    /******/    // mode & 4: return value when already ns object
    /******/    // mode & 8|1: behave like require
    /******/    __webpack_require__.t = function(value, mode) {
    /******/        if(mode & 1) value = __webpack_require__(value);
    /******/        if(mode & 8) return value;
    /******/        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    /******/        var ns = Object.create(null);
    /******/        __webpack_require__.r(ns);
    /******/        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
    /******/        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
    /******/        return ns;
    /******/    };
    /******/
    /******/    // getDefaultExport function for compatibility with non-harmony modules
    /******/    __webpack_require__.n = function(module) {
    /******/        var getter = module && module.__esModule ?
    /******/            function getDefault() { return module['default']; } :
    /******/            function getModuleExports() { return module; };
    /******/        __webpack_require__.d(getter, 'a', getter);
    /******/        return getter;
    /******/    };
    /******/
    /******/    // Object.prototype.hasOwnProperty.call
    /******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    /******/
    /******/    // __webpack_public_path__
    /******/    __webpack_require__.p = "";
    /******/
    /******/
    /******/    // Load entry module and return exports
    /******/    return __webpack_require__(__webpack_require__.s = "./src/index.js");
    /******/ })
    /************************************************************************/
    /******/ ({
    
    /***/ "./src/index.js":
    /*!**********************!*\
      !*** ./src/index.js ***!
      \**********************/
    /*! no exports provided */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    __webpack_require__.r(__webpack_exports__);
    /* harmony import */ var _someVar__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./someVar */ "./src/someVar.js");
    
    console.log(_someVar__WEBPACK_IMPORTED_MODULE_0__["default"]);
    
    /***/ }),
    
    /***/ "./src/someVar.js":
    /*!************************!*\
      !*** ./src/someVar.js ***!
      \************************/
    /*! exports provided: default */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    __webpack_require__.r(__webpack_exports__);
    /* harmony default export */ __webpack_exports__["default"] = ('someVar');
    
    /***/ })
    
    /******/ });
    

    which, simplified, looks like:

    (function (modules) {
        // (code that manages the modules)
    })
        ({
    
            "./src/index.js":
                (function (module, __webpack_exports__, __webpack_require__) {
                    __webpack_require__.r(__webpack_exports__);
    /* harmony import */ var _someVar__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./someVar */ "./src/someVar.js");
                    console.log(_someVar__WEBPACK_IMPORTED_MODULE_0__["default"]);
                }),
            "./src/someVar.js":
                (function (module, __webpack_exports__, __webpack_require__) {
                    __webpack_require__.r(__webpack_exports__);
    /* harmony default export */ __webpack_exports__["default"] = ('someVar');
    
                })
    
        });
    

    The someVar is not exposed globally; it’s inside the IIFE that Webpack generates. So, it’s not visible to an inline <script> tag on the page.

    For a similar reason, the following code does not result in any global pollution:

    ((modules) => {
      const getModule = (moduleName) => modules[moduleName];
      modules.main(getModule);
    })({
      main: (getModule) => console.log('main running can see ' + getModule('foo')),
      foo: 'foo content'
    });

    This is a good thing. If modules were exposed globally, naming collisions could be frequent, especially with large sites with tons of separate scripts with different functionalities.

    Put all of your code that depends on the library in modules too, eg, change our index.js to

    import * as scripts from './scripts.js';
    scripts.foo();
    
    Reply

Leave a Comment