Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Goog.Module: multiple entry points don't work properly #52

Open
samilyak opened this issue May 25, 2018 · 8 comments
Open

Goog.Module: multiple entry points don't work properly #52

samilyak opened this issue May 25, 2018 · 8 comments

Comments

@samilyak
Copy link

Hi guys,

I have an issue with a multi-module build (I'm talking about output modules here related to code splitting and --module flag).

Here's my webpack config:

var path = require('path');
var webpack = require('webpack');
var ClosurePlugin = require("closure-webpack-plugin");

module.exports = {

  context: path.resolve(__dirname),

  entry: {
    'module1': './module1.js',
    'module2': './module2.js',
  },

  output: {
    filename: '[name].build.js',
    path: path.resolve(__dirname, '../www/js')
  },

  plugins: [
    new ClosurePlugin({
      mode: 'AGGRESSIVE_BUNDLE',
      closureLibraryBase: require.resolve('google-closure-library/closure/goog/base'),
      deps: [
        path.resolve(__dirname, './my-deps.js')
      ]
    }, {
      compilation_level: 'WHITESPACE_ONLY',
    }),

    new webpack.optimize.CommonsChunkPlugin({
      name: 'module1',
      minChunks: 2
    })
  ]

};

module1.js:

goog.provide('module1');

console.info('module1');

module2.js:

goog.provide('module2');
goog.require('module1');

console.info('module2');

Here's the output generated into module2.build.js:

webpackJsonp([0], function(__wpcc){'use strict';goog.provide("module2");goog.require("module1");console.info("module2");});

The problem is the inner function is never called.

When both module1.build.js and module2.build.js are linked to the page, I only see module1 string in the console output.
There's no module2 string.

This behaviour is reproducible for any number of modules more than 1 and any compilation_level.


On the other hand, if I comment out ClosurePlugin in the config, both modules run when loaded.
Here's module2.build.js in this case:

webpackJsonp([1],[
/* 0 */,
/* 1 */
/***/ (function(module, exports) {

goog.provide('module2');
goog.require('module1');

console.info('module2');

/***/ })
],[1]);

For this one I see both module1 and module2 strings in the console output.

This example covers only a case with 1 output module.


I feel like it's not an intended behaviour, that's why opened this issue.

I have node v8.9.4 on OSX 10.12.6.

@ChadKillingsworth
Copy link
Member

The difficulty here is you are attempting to both code split and directly reference. This probably should have been an error.

@samilyak
Copy link
Author

@ChadKillingsworth well, I think this is more or less a common use-case.
Main module (module1) contains some low-level functionality, while other modules loaded separately (module2) might use that functionality.

@ChadKillingsworth
Copy link
Member

This is a deficiency in the goog.module pattern. To my knowledge, there isn't a dynamic loading function similar to import() or require.ensure(). I think you would need to use one of those methods to load the child chunk.

The clue here was that you used an entry point to force splitting code. You should not really do that. It will duplicate the polyfills and cause the issues you noticed.

You can use goog.module.get in the child chunk to reference a goog.module that was loaded with the parent chunk.

@samilyak
Copy link
Author

In my use-case I don't load resulting modules dynamically via js.

I load them via static <script> tag on different pages,
i.e. <script src="module1.build.js"> is always in html on any page
while <script src="module2.build.js"> is in html only on a specific page which module2 is responsible for (this is just a simplest example of course).

The clue here was that you used an entry point to force splitting code. You should not really do that.

Why? I had an impression that entry array in config is treated by this plugin as closure modules.

Otherwise I see no other way to tell the plugin how I'd like to split my result build.
Is this correct?

My current build process is based on plovr and its modules config key.
It does exactly what I described and takes care about code reshuffling across resulting modules (leveraging the dependancy tree between modules).
I was just considering closure-webpack-plugin as a replacement for it.

Do you think it's achievable in a current version of the plugin, or there's no way to make it work the way I want at the moment?

@ChadKillingsworth ChadKillingsworth changed the title All but 1st result modules are not running Goog.Module: multiple entry points don't work properly May 29, 2018
@ChadKillingsworth
Copy link
Member

It's possible to handle this. However I'm also going to be investigating the possibility of a loader that converts goog modules into standard CommonJS.

@Dan1ve
Copy link

Dan1ve commented Jun 12, 2019

Any news about this?

I think I have the same problem. My config is quite similar to the example referenced above, except for the entry section:

entry: {
    viewA: './src/ViewA/Index.js',
    viewB: './src/ViewB/Index.js'
 },

I use goog.modules instead of goog.provide consistently in my code.
Interestingly,

  • everything seems to work fine in the dev-server-mode
  • npm run build works fine if only one entry point is specified

Any hints where this might go wrong in the plugin's code?

@ChadKillingsworth
Copy link
Member

The issue is that there exists a set of goog.modules that are shared between 2 different entry points. Those need split off into a common chunk. To do that with webpack, you need to have a dynamic loading statement that allows webpack to split the common files out into a unique chunk.

With ES Modules you would do something like:

./module1.js

import('./common.js').then(commonExports => {
  console.log(commonExports.default);
});

Webpack would automatically code split at this point and create separate JS modules. For goog modules, the plugin would have to be updated to recognize this type of late loading behavior as well.

You could try something like:

./module1.js

goog.forwardDeclare('common');
import('./common.js').then(commonExports => {
  console.log(common.foo);
});

I'm not sure that will work, but that's the best option I have.

@DreierF
Copy link
Contributor

DreierF commented Jan 10, 2020

@ChadKillingsworth Thanks for your response!

This unfortunately does not seem to work ATM. I created a minimal example project here that reproduces the crash: DreierF#1

The problem seems to be that the dynamically loaded chunks do not point to the entry point chunk as their parent and therefore the source files of the dynamic chunk are not passed to the closure compiler. As they are also no entry points themselves they never participate in any compilation.
Was just an issue with a destructuring assignment. Please have a look at my PR

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

No branches or pull requests

4 participants