Skip to content
This repository has been archived by the owner on Oct 9, 2020. It is now read-only.

Bundling webpack UMD module breaks execution of bundled file #614

Closed
psurrey opened this issue May 31, 2016 · 21 comments
Closed

Bundling webpack UMD module breaks execution of bundled file #614

psurrey opened this issue May 31, 2016 · 21 comments

Comments

@psurrey
Copy link

psurrey commented May 31, 2016

Hi,

I'm trying to include a dependency into my application that comes bundled using webpack "umd" target (the dependency is https://www.npmjs.com/package/react-data-grid).
I've managed to include it into my app in development mode (modules are loaded by systemjs on the fly asynchronously). In my production build (using angular-cli, I'm developing an app with angular2) this module seems to mess up the bundled file (main.js).
With some debugging effort I was able to figure out that the webpack module is included in the bundle using an anonymous function and not with the usual System.register calls. In systemjs after eval runs past this function the "execute" function property of the current load object is undefined:

Is it even possible to load such a UMD module packaged by webpack?

Thanks in advance for your answers!

@psurrey psurrey changed the title Loading webpack UMD module Bundling webpack UMD module breaks execution of bundled file May 31, 2016
@psurrey
Copy link
Author

psurrey commented May 31, 2016

I have changed the webpack configuration of the react-data-grid library to libraryTarget "commonjs2" (see https://webpack.github.io/docs/configuration.html#output-librarytarget), built the project locally and used that dist file in my project => this works perfectly.

@guybedford
Copy link
Member

I take it you mean this file - https://npmcdn.com/react-data-grid@0.14.42/dist/react-data-grid.js.

If I save that file as react-data-grid.js and add map: { 'react': '@empty', 'react-dom': '@empty' } for simplicity sake and bundle with jspm bundle react-data-grid.js, it seems to work fine to me?

@psurrey
Copy link
Author

psurrey commented May 31, 2016

@guybedford Thanks for your fast response! In the meantime I have setup a repo to demonstrate the issue: https://github.com/psurrey/systemjs-umd

Can you reproduce it? maybe the issue is with my setup?

@guybedford
Copy link
Member

Try adding - System.config({ transpiler: false }) to the production configuration.

This is actually a bug of sorts. I've added a fix to avoid it with systemjs/systemjs@a1decd7.

@psurrey
Copy link
Author

psurrey commented Jun 1, 2016

Thanks!
Adding System.config({ transpiler: false }) solves the issue in my demo repository, but not in my real application... I will try to understand why and if required extend the demo project to show the issue again.

@psurrey
Copy link
Author

psurrey commented Jun 2, 2016

Ok, I did some more debugging and I found that in my example the "main.js" file which should be executed when imported from index.html, is always at the end of the bundled file:

...
    System.registerDynamic("vendor/react-data-grid/index.js", ["./dist/react-data-grid"], !0, function(a, b, c) {
        return c.exports = a("./dist/react-data-grid"),
        c.exports
    }),
    System.registerDynamic("main.js", ["@angular/core", "@angular/platform-browser-dynamic", "react", "react-data-grid"], !0, function(a, b, c) {
        var d = a("@angular/core")
          , e = a("@angular/platform-browser-dynamic");
        console.log("core:", d),
        console.log("platform:", e),
        console.log("In index.js");
        var f = a("react");
        console.log(f);
        var g = a("react-data-grid");
        return console.log(g),
        c.exports
    });
})(System, System);

But in the bundle of my "real" application the registerDynamic for "main.js" is called somewhere in the middle of the file:


    System.registerDynamic("vendor/angular2-cookie/core.js", ["@angular/core", "./services"], !0, function(a, b, c) {
        "use strict";
        function d(a) {
            for (var c in a)
                b.hasOwnProperty(c) || (b[c] = a[c])
        }
        var e = a("@angular/core")
          , f = a("./services");
        return d(a("./services")),
        b.ANGULAR2_COOKIE_PROVIDERS = [e.provide(f.CookieOptions, {
            useClass: f.BaseCookieOptions
        })],
        c.exports
    }),
    System.registerDynamic("main.js", ["@angular/platform-browser-dynamic", "@angular/core", "@angular/http", "@angular/router", "angular2-cookie/core", "./app"], !0, function(a, b, c) {
        "use strict";
        var d = a("@angular/platform-browser-dynamic")
          , e = a("@angular/core")
          , f = a("@angular/http")
          , g = a("@angular/router")
          , h = a("angular2-cookie/core")
          , i = a("./app");
        return i.environment.production && e.enableProdMode(),
        d.bootstrap(i.LeanixPathfinderWebAppComponent, [f.HTTP_PROVIDERS, g.ROUTER_PROVIDERS, h.CookieService, i.LX_BASE_SERVICES, i.authProvide]).then(function() {
            document.getElementById("Splashscreen-container").remove()
        }),
        c.exports
    }),
    System.registerDynamic("vendor/@angular/http/src/http.js", ["../src/facade/lang", "../src/facade/exceptions", "@angular/core", "./interfaces", "./static_request", "./base_request_options", "./enums"], !0, function(a, b, c) {
        "use strict";

I suppose it should always be the last module which is registered!? But I have no idea why it isn't in my "real" application...

@guybedford
Copy link
Member

Registration order shouldn't be a problem. You could try manually reordering the bundle and see if that recreates the issue, but I somehow doubt that would be it?

@filipesilva
Copy link

filipesilva commented Jun 3, 2016

I'm getting a very similar issue over at Angular-CLI: angular/angular-cli#951.

After adding moment with just a mapping in the SystemJS configuration, loading the app dynamically via SystemJS works but bundling doesn't - the app doesn't even load, and as far as I can tell for the same reason as shown by @psurrey.

After explicitly adding the module format for moment, bundling results in a working app again.

It strikes me as odd that SystemJS loads moment correctly at runtime without the module format information, but that bundling requires it or else it breaks.

@psurrey
Copy link
Author

psurrey commented Jun 3, 2016

@filipesilva Thanks for you information! Explicitly specifying the module format fixes the issue also in my case.

But stillt it is interesting to understand why the bundled version behaves differently than the dynamically loaded app via SystemJS.

@filipesilva
Copy link

@psurrey did you have to specify the module formats for all modules? I've an user that is also reporting that main.js ended up in the middle of a file angular/angular-cli#917 (comment)

@psurrey
Copy link
Author

psurrey commented Jun 6, 2016

@filipesilva No, not for all modules. Just for the ones that I've included since it stopped working, which in my case were four react-specific modules.

@filipesilva
Copy link

filipesilva commented Jun 9, 2016

I think that we inadvertently fixed this issue when we changed the way we bundle things in this change: angular/angular-cli@5f909aa#diff-93761c20921e800376ba6d58ea4b4988

I just tested it using our momentjs tutorial: https://github.com/angular/angular-cli/wiki/3rd-party-libs#adding-momentjs-library-to-your-project by leaving out the code that adds package format:

/** User packages configuration. */
const packages: any = {
  'moment':{
    format: 'cjs'
  }
};

In the latest beta (1.0.0-beta.5), we made two bundles:

  • 'main - [app/**/*]' -> 'main.js' (everything but the app went into main)
  • 'app - (app/**/*.js - [app/**/*.js])' -> 'app/index.js' (the app went into app/index.js)

When running ng build -prod, the registerDynamic for "main.js" is found in the middle of the main.js bundle instead of at the end, and app/index.js is never loaded. Adding the package format for moment corrects this behaviour.

In the latest master version, we only generate one bundle: 'main' -> 'main.js'. With a single bundle, the registerDynamic for "main.js" is found in the end of the main.js bundle, and everything works correctly without needing to define a package format for moment.

I'm not sure if my bundling logic was originally flawed or if this represents a bug in the SystemJS builder, but I wanted to leave these details here just in case.

Thanks for the great work @guybedford, SystemJS is an awesome tool!

@guybedford
Copy link
Member

Thanks @filipesilva for the explanation here. Yes altering the format of moment would have triggered require detection in the source, which may have altered the tree structure. Ordering of the build output is based on the build tree traversal (post-order), so that tree structure changes will alter the order of the output.

It doesn't sound like there was a specific bug here, and that things are working now. But if there are still any issues or anything that is unclear, please let me know.

@filipesilva
Copy link

@guybedford there is a bit that confuses me. In my head, it is not clear why separating a bundle would result into non-functioning bundles - and why setting the module format of one of the used libs would fix the modules.

Intuitively I would think that if a single bundle worked, having that bundle split would also work.

@guybedford
Copy link
Member

@filipesilva I don't think you were having the same issue here as @psurrey. In order to understand what sort of execution error this was, it may be worth running some debugging on the bundle execution to see if the main entry point is properly executing in the silent error case. Without actively debugging, I can only guess at what the issue might be.

If I were to guess I would say that in theory the order of modules in a bundle should not matter. But it is very easy to write code that makes global assumptions where code execution order does matter and changes of order then affect things. These bugs can be sporadic and difficult to track down, making that a possibility here.

@filipesilva
Copy link

filipesilva commented Jun 10, 2016

@guybedford I will try to come back with a working, debuggable example repo that does not use Angular-CLI.

@guybedford
Copy link
Member

Sure, please do!

@filipesilva
Copy link

@guybedford I was finally able to set up a working repro of this bug. Knowing that it was related to the dual bundle setup was the missing piece.

Here is the repo: https://github.com/filipesilva/systemjs-builder-issue. To repro, clone it, npm i then npm start.

On another console, run npm run build-one-bundle and open http://localhost:8080/ on a browser. You should see number is 42.

Now run npm run build-two-bundles, empty cache and hard reload on the browser, and you should only see loading....

Open system.config.js, and uncomment // 'moment': { format: 'cjs' }. Run npm run build-two-bundles, empty cache and hard reload on the browser, and you should only see number is 42 again.

It seems that something breaks because a module on the second bundle needs something that isn't wrapped in registerDynamic on the first bundle, but it's now in a different order than if it were a single bundle.

@guybedford
Copy link
Member

Thanks @filipesilva I will go through this when I can.

@guybedford
Copy link
Member

@filipesilva it turns out this was a SystemJS bug, I added a fix in systemjs/systemjs@b12938b.

@filipesilva
Copy link

Oh man I thought I was going crazy with that thing. Glad to hear there's a fix, cheers!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants