5

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?

yevg
  • 1,846
  • 9
  • 34
  • 70
  • The whole point of Webpack and modules is to not pollute the global scope, thus `scripts` is not visible to an inline ` – CertainPerformance Feb 19 '21 at 00:09
  • @CertainPerformance ok I think I understand. But then what *is* the point of webpack in this context. If the libraries I import via webpack are not exposed to a given web page in the markup, then Im not sure I get the use-case – yevg Feb 19 '21 at 00:16
  • Write all of *your* script with modules, integrated with Webpack. Eg, add to `index.js`, `scripts.foo()`. – CertainPerformance Feb 19 '21 at 00:18
  • but `scripts.foo()` is effectively added to `index.js` via the `import` statement - then after running weback, I can see these function added to `bundle.js`, within an `eval` statement, which is referenced globally via tthe script tag src. it stands to reason that these functions should be exposed to all JS below, but they are not – yevg Feb 19 '21 at 00:23
  • The whole of the bundle is inside an IIFE. It's a bit like `(() => { const scripts = (() => { /* code of library */ })(); /* index.js starts */ scripts.foo(); })();` Its internals are not exposed outside. This is why you could theoretically have as many separate Webpack bundles on the page as you wanted, without resulting in global variable collisions. – CertainPerformance Feb 19 '21 at 00:26

2 Answers2

0

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();
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
0

You have to add output.library option in your webpack configuration. Look at this article: https://webpack.js.org/guides/author-libraries/

Max
  • 541
  • 6
  • 9