I tried this the last two days and I can't get it to work like expected: I want to build my own JavaScript library and register it under an already existing namespace ("OCA" - in this particular case). And as you might understand, I don't want to be forced to go without modern approaches like type safety through typescript or modules.
Therefore I use webpack 2 and the libraryTarget: umd
to register the output under "OCA.Ocr" (my library is named "Ocr"). This works like intended, but when it comes to the point that I want to use for example underscorejs, as it will be available globally in the application the library should be also delivered to, I cannot get it to work.
I followed the webpack configuration documentation and it says that the externals configuration option should be the way to go:
externals: { // object
angular: "this angular", // this["angular"]
react: { // UMD
commonjs: "react",
commonjs2: "react",
amd: "react",
root: "React"
}
}
// Don't follow/bundle these modules, but request them at runtime from the environment
I used it like proposed by the guide but it doesn't work:
/* global __dirname, require, module*/
const webpack = require("webpack");
const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
const path = require("path");
module.exports = function (env) {
let target = env.target;
let libraryName = ["OCA", "Ocr"];
let plugins = [];
let outputFile;
if (target === "production") {
plugins.push(new UglifyJsPlugin({ minimize: true }));
}
outputFile = "ocr[name].min.js";
const config = {
entry: {
app: __dirname + "/src/app.ts",
personal: __dirname + "/src/personal.ts"
},
output: {
path: __dirname + "/dist",
filename: outputFile,
library: libraryName,
libraryTarget: "umd",
umdNamedDefine: true
},
module: {
rules: [
{
test: /\.ts$/,
enforce: "pre",
loader: "tslint-loader",
options: {
tsConfigFile: "tsconfig.app.json",
}
},
{
test: /\.ts?$/,
loader: "ts-loader",
exclude: /node_modules/,
options: {
configFileName: "tsconfig.app.json"
}
}
],
},
resolve: {
modules: [path.resolve("./src")],
extensions: [".ts"],
},
externals: {
underscore: { // UMD
commonjs: "underscore",
commonjs2: "underscore",
amd: "underscore",
root: "_"
}
},
plugins: plugins,
};
return config;
}
My app.ts
file which uses the underscore library (for example the _.defer
method, which of course is not always the best to use) looks like that:
import _ from 'underscore';
export class App {
constructor() {
_.defer(() => {
console.log('test');
});
}
}
export let $app: App = new App();
I included it in the application and also checked that the underscorejs library is getting loaded before my lib gets loaded by the browser, but the console output still states:
TypeError: underscore_1.default is undefined
The compiled output is the following (maybe this helps a little bit):
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("underscore"));
else if(typeof define === 'function' && define.amd)
define("Ocr", ["underscore"], factory);
else if(typeof exports === 'object')
exports["Ocr"] = factory(require("underscore"));
else
root["OCA"] = root["OCA"] || {}, root["OCA"]["Ocr"] = factory(root["_"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {
return /******/ (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;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // 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 = 2);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */,
/* 1 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var underscore_1 = __webpack_require__(1);
var App = (function () {
function App() {
underscore_1.default.defer(function () {
console.log('test');
});
}
return App;
}());
exports.App = App;
exports.$app = new App();
/***/ })
/******/ ]);
});
Does anyone know how this is working and what I will have to do? I am completely lost and now hoping for your help.
Btw: This is also not working for me.
I have the same issue as you, however, if you set the property var in the libraryTarget option, the variable stops being undefined. Maybe this will help you:
externals: {
"lodash": {
var:'_'
}
}
You have 2 options here. I recommend option #1.
In fact, if you use UMD
and plan on supporting node
(in addition to commonjs, amd, and browser), always set globalObject: 'this'
output.globalObject
to this
, and use externals.root
.const config = {
output: {
library: libraryName,
libraryTarget: "umd",
globalObject: 'this' // <-- THIS IS THE IMPORTANT LINE FOR UMD+NODE
},
externals: {
underscore: { // UMD
commonjs: "underscore",
commonjs2: "underscore",
amd: "underscore",
root: "_"
}
},
};
output.globalObject
When targeting a library, especially when the libraryTarget is 'umd', this option indicates what global object will be used to mount the library. To make UMD build available on both browsers and Node.js, set output.globalObject option to 'this'.
externals.var
instead of externals.root
.
externals: {
underscore: { // UMD
commonjs: "underscore",
commonjs2: "underscore",
amd: "underscore",
var: "_"
}
},
This is a workaround and does not require setting globalObject: 'this'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With