jayphelps / webpack-rxjs-externals

Generate all the RxJS v5 "externals" for your webpack config.
17 stars 6 forks source link

Property of Undefined #3

Open patrickhousley opened 7 years ago

patrickhousley commented 7 years ago

Getting error Cannot read property 'Subject' of undefined when using this webpack module.

RxJS: 5.3.0 Webpack 2.4.1 Angular: 4.0.2

jayphelps commented 7 years ago

Hey @patrickhousley, I'd need some more details to help. Anything you can provide is helpful--including but not limited to: how are you using webpack-rxjs-externals? What is the stack trace including filename/lines?

In addition, if possible, can you make a simple jsbin, codepen, etc to demo? It's not necessarily required, but without it might be really hard for me to understand how you're running into this.

patrickhousley commented 7 years ago

Please use repository/branch: https://github.com/patrickhousley/angular2-aot-webpack/tree/test/webpack-rxjs-externals/issues/3

jayphelps commented 7 years ago

@patrickhousley how do I reproduce with that repo?

patrickhousley commented 7 years ago

Clone the repository. Checkout the test/webpack-rxjs-externals/issues/3 branch. Run npm start. Open locahost:9000 in the browser and see the error in the console.

patrickhousley commented 7 years ago

I forgot to update that repo with a script tag to rxjs. Let me update the branch.

patrickhousley commented 7 years ago

Added the script tag to the index.html file. The repository should be good for testing now.

jayphelps commented 7 years ago

hmmm this seems like an application build using webpack--which isn't what webpack-rxjs-externals is intended for. Webpack externals is for dynamically linking external libraries which are not bundled with your webpack code--usually that means including files from a CDN or for libraries which want to share peer dependencies in their UMD builds.

If you provide externals it's my understanding that webpack will not bundle anything that matches those externals directly with your code. Instead, you're basically saying "dynamically link this external library at runtime, this is how to find them". If you remove the addition of the webpack externals, everything works as expected for me.

Since you mentioned the script tag to rxjs, this suggests you are indeed trying to dynamically link RxJS via CDN. Is that correct? That's why you're using webpack-rxjs-externals?

jayphelps commented 7 years ago

If that is indeed the case, I don't believe I can help much further without diving super deep into your specific build chain. something is telling webpack to try and statically link the externals. (total guess) maybe one of your typescript loaders. I really don't know, though.

The output from webpack is simply undefined. This is rxjs/Subject, for instance

screen shot 2017-04-21 at 4 59 39 pm

It's definitely possible a problem with webpack-rxjs-externals, but I would bet against it--I'm clearly biased though hehe :clown_face: I really didn't intend for it to be used by application builds, though, so I don't have cycles to dig super deep in a complex build setup. Sorry man! 😢

jayphelps commented 7 years ago

We use this primarily for redux-observable. Here's an example of the expected output of the RxJS externals depended on for redux-observable builds:

(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory(require("rxjs/Observable"), require("rxjs/Subject"), require("rxjs/operator/filter"), require("rxjs/operator/map"), require("rxjs/operator/switchMap"), require("rxjs/observable/from"), require("rxjs/observable/merge"), require("rxjs/observable/of"));
    else if(typeof define === 'function' && define.amd)
        define(["rxjs/Observable", "rxjs/Subject", "rxjs/operator/filter", "rxjs/operator/map", "rxjs/operator/switchMap", "rxjs/observable/from", "rxjs/observable/merge", "rxjs/observable/of"], factory);
    else if(typeof exports === 'object')
        exports["ReduxObservable"] = factory(require("rxjs/Observable"), require("rxjs/Subject"), require("rxjs/operator/filter"), require("rxjs/operator/map"), require("rxjs/operator/switchMap"), require("rxjs/observable/from"), require("rxjs/observable/merge"), require("rxjs/observable/of"));
    else
        root["ReduxObservable"] = factory(root["Rx"], root["Rx"], root["Rx"]["Observable"]["prototype"], root["Rx"]["Observable"]["prototype"], root["Rx"]["Observable"]["prototype"], root["Rx"]["Observable"], root["Rx"]["Observable"], root["Rx"]["Observable"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_5__, __WEBPACK_EXTERNAL_MODULE_6__, __WEBPACK_EXTERNAL_MODULE_7__, __WEBPACK_EXTERNAL_MODULE_8__, __WEBPACK_EXTERNAL_MODULE_9__, __WEBPACK_EXTERNAL_MODULE_10__, __WEBPACK_EXTERNAL_MODULE_11__) {
/// etc

Notice how it correctly handles all the situations, commonjs commonjs2, amd or global

mattlewis92 commented 7 years ago

I just did some digging on this and it seems like while externals is primarily for library developers one use case it should enable is to allow libs to be loaded via a cdn. Modifying this lib to be

if (request.startsWith('rxjs/')) {
  let root = rootForRequest(request);
  if (Array.isArray(root)) {
    root = root.join('.');
  }
  return callback(null, root);
}

Fixes that project. There's a couple API options I can see off the top of my head to enable this use case:

Either bake this feature into the library as an option:

rxjsExternalsFactory({scriptTagExternals: true}); //  there's probably a better name

or a more flexible solution, but probably a bit OTT:

rxjsExternalsFactory({
  modifyExternals(external) {
    /* external is the value of 
      {
        root: rootForRequest(request),
        commonjs: request,
        commonjs2: request,
        amd: request
      }
    */
    let root = external.root;
    if (Array.isArray(root)) {
      root = root.join('.');
    }
    return root;
  }
})

@jayphelps what do you think?

patrickhousley commented 7 years ago

From the webpack docs:

The bundle with external dependencies can be used in various module contexts, such as CommonJS, AMD, global and ES2015 modules. The external library may be available in any of these forms:

global - An external library can be available as a global variable. The consumer can achieve this by including the external library in a script tag. This is the default setting for externals.
commonjs - The consumer application may be using a CommonJS module system and hence the external library should be available as a CommonJS module.
commonjs2 - Similar to the above line but where the export is module.exports.default.
amd - Similar to the above line but using AMD module system.

Maybe this project could use the same distinction built into it.

@mattlewis92 To your point, externals is primarily for library developers. However, it also works extremely nicely in a Typescript environment where the deployment environment already contains some of the vendor modules on the window (like rxjs).

patrickhousley commented 7 years ago

One thing you might also add to the readme is a warning that this also does not work when importing from rxjs. Example, import { Observable } from 'rxjs';

fahad19 commented 7 years ago

Ran into similar issues too. fixed by setting libraryTarget to umd.

PR here for more details: https://github.com/Travix-International/frint/pull/332