jantimon / html-webpack-plugin

Simplifies creation of HTML files to serve your webpack bundles
MIT License
10.72k stars 1.31k forks source link

Wrong chunks order also with chunksSortMode: 'none' #481

Closed Ks89 closed 7 years ago

Ks89 commented 8 years ago

I want to create two html pages based on ejs templates, injecting chunks into the body tag.

Pages are created and everything seems to be ok, but the order of chunks is wrong.

If I use chunksSortMode:'auto' for both files -> the first is ok (polyfills->vendor->app), but the second one no (admin->polyfills->vendor)

If I use chunksSortMode:'none' for both files -> both of them are wrong. The first one is (vendor->app->polyfills), the second one (vendor->admin->polyfills)

For the first file (with app) I can use 'auto', but for the second one, I'm obliged to create a crazy-custom-order function [p..-v...-a... (not alphabetical order)] (that i don't want to post here, because it's something the no one should see :( ).

new HtmlWebpackPlugin({
      title: TITLE,
      inject: true,
      chunksSortMode: 'auto', //ok only with auto
      chunks: ['polyfills', 'vendor', 'app'],
      template: TEMPLATE_PATH,
      filename: TEMPLATE_HTML
    }),
    new HtmlWebpackPlugin({
      title: TITLE_ADMIN,
      inject: true,
      chunksSortMode: 'none' //wrong with every value -> I'm using a crazy order function (very bad)
      chunks: ['polyfills', 'vendor', 'admin'],
      template: TEMPLATE_ADMIN_PATH,
      filename: TEMPLATE_ADMIN_HTML
    }),

The expected behaviour should be this:

Every other behaviour with chunksSortMode: 'none' it is weird.

PS: I created my chunks in this way (probably, the cause of this issue is here...I really don't know):

entry: {
    polyfills: './src/polyfills.ts',
    vendor: './src/vendor.ts',
    app: './src/main.ts',
    admin: './src/admin.ts'
  },
........
new CommonsChunkPlugin({
      name: ['admin', 'app', 'vendor', 'polyfills'],
      minChunks: Infinity
    }),

I used: Nodejs 7.0.0, macOS Sierra, Webpack 2.1.0 beta 25, HtmlWebpackPlugin: 2.24.1

Thank u, Ks89

Sayan751 commented 8 years ago

I am also facing the same issue, and described the problem in details here.

trungnguyenn commented 8 years ago

I used chunksSortMode with custom function and it worked for me as a trick (alphabetical order)

chunks: ['vendor', 'polyfills', 'admin'],
chunksSortMode: function(a, b) {
   return (a.names[0] < b.names[0])? 1 : -1;
}
Ks89 commented 8 years ago

Yes. Custom functions are working, but not the other values.

humorHan commented 8 years ago

This can only be a simple choice if it is positive or reverse. Read the source to find in the function and then called the sort function, not too friendly, has been feedback

jantimon commented 8 years ago

none means that it takes the sort order from the webpack compiler.

Ks89 commented 8 years ago

The problem is not that these functions aren't intuitive. The problem is that they are broken, as explained in my post. It's also possible that this is a webpack issue. If 'none' means 'the same order of webpack' , this means that webpack has a bug. I don't know. But how can we check where is the problem? Do you want a skeleton project to see this problem?

jantimon commented 8 years ago

Feel free to contribute :) Add some tests install iron-node or setup the node inspector and open a pull request. Please make sure that you support webpack1, webpack2 and don't break backwards compatibility

humorHan commented 8 years ago

'function', in alphabetical order, because the sort method is called internally.

Ks89 commented 8 years ago

Yes, sorry. Wrong word. I called functions the strings none and so on.

humorHan commented 8 years ago

@Ks89 'function', in alphabetical order, because the sort method is called internally. Will the next version of the problem be solved

jantimon commented 8 years ago

Sorry @humorHan I really don't understand what you are trying to say

humorHan commented 8 years ago

@jantimon Well, maybe I'll describe some of the problems, um... I'll describe it again. For example, I have three JS files, I want to insert in a specific order to the HTML, but by setting a variety of chunksSortMode and can not be achieved. for example: a.js b.js c.js, I want to insert the HTML to achieve the following results :

<script src="./b.js"></script> <script src="./a.js"></script> <script src="./c.js"></script></body> but,failed.

By looking at the source code to understand, it is possible because the source code inside the sort function, the loss of the flexibility of the parameters. If you really can not understand it does not matter, I temporarily change the plugin source code to achieve the effect I want. Thank you very much to answer questions!

jantimon commented 8 years ago

You can pass a function which is in full control of sorting your assets. Didn't that work for you?

humorHan commented 8 years ago

@jantimon I tried to write a few function, but did not achieve the effect, whether it is because of the inside of the plug-in called the sort method? I think, should remove the internal call ‘sort’ function to increase the flexibility. that is, to remove the index.js source sort method, as follows:

index.js : about 335 line

if (typeof sortMode === 'undefined') { sortMode = 'auto'; } // Custom function if (typeof sortMode === 'function') { return chunks.sort(sortMode); // TODO Modify as: return sortMode(chunks); } // Disabled sorting: if (sortMode === 'none') { return chunkSorter.none(chunks);

}

jantimon commented 8 years ago

Haha no no that's fine.

Please read here how to write a comparison method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

jamesjieye commented 8 years ago

I did something like this to customize my chunk ordering. Basically, it will order the chunk the way you put the chunks in the array.

chunksSortMode: function (chunk1, chunk2) {
  var orders = ['manifest', 'style', 'vendor', 'app'];
  var order1 = orders.indexOf(chunk1.names[0]);
  var order2 = orders.indexOf(chunk2.names[0]);
  if (order1 > order2) {
    return 1;
  } else if (order1 < order2) {
    return -1;
  } else {
    return 0;
  }
}
Ks89 commented 8 years ago

Finally, I created a skeleton project, as promised: https://github.com/Ks89/Angular2-webpack2-skeleton

Branches:

run npm run buildDev and check ./dist/admin.html to see if the order is ok or not.

humorHan commented 7 years ago

@jamesjieye If I want to achieve the following order, does your ordering function work? //some codes

<script src="./manifest.js"></script> 
<script src="./styl. js"></script>
 <script src="./vendor.js"></script> 
<script src="./app.js"></script>
jamesjieye commented 7 years ago

@humorHan

Yes, by specifying the order like this: var orders = ['manifest', 'style', 'vendor', 'app'];, the scripts will be like this.

<script src="./manifest.js"></script> 
<script src="./style. js"></script>
<script src="./vendor.js"></script> 
<script src="./app.js"></script>

BTW, if you want to exlcude style.js in the output, you can use this plugin https://github.com/jamesjieye/html-webpack-exclude-assets-plugin.

humorHan commented 7 years ago

@jamesjieye ok! thank you~

bholben commented 7 years ago

@jamesjieye's custom function above works very nicely. If we step back and look at the root issue, I'm thinking the better solution is to design webpack such that the entry property can accept either an object or an array. The fact that we have to cobble together something to control order seems kind of silly to me.

timc13 commented 7 years ago

yes! a sensible default ordering should be the array order.

jamesjieye commented 7 years ago

@bholben totally agree! When I wanted to order the chunks, I was thinking passing an array to define the order. It would be much easier to use if the custom order function was built into the plugin itself.

humorHan commented 7 years ago

@jamesjieye yes,I also agree that once I tried to change the plug-in source code, can be sorted in accordance with the order of the incoming array, I feel more convenient

anthonyettinger commented 7 years ago

Thanks to @jamesjieye

      chunksSortMode: function (chunk1, chunk2) {
        var orders = ['vendor', 'app'];
        var order1 = orders.indexOf(chunk1.names[0]);
        var order2 = orders.indexOf(chunk2.names[0]);

        return order1 - order2;
      }

ugly but works. i spent awhile trying to figure out raw-loader only worked once in awhile.

kamihouse commented 7 years ago

Tks @jamesjieye && @anthonyettinger, these are my settings for define resource sorting:

new HtmlWebpackPlugin({
    title: 'App Name',
    minify: {
        collapseWhitespace: true,
        keepClosingSlash: true,
        removeComments: true
    },
    chunksSortMode: (c1, c2) => {
        // Corrige bug da ordenação de assets.
        let orders = ['common', 'vendor', 'app'];
        let o1 = orders.indexOf(c1.names[0]);
        let o2 = orders.indexOf(c2.names[0]);
        return o1 - o2;
    },
    filename: '../index.html',
    favicon: './src/assets/images/favicon.ico',
    template: './src/assets/index.ejs'
})
zhe-he commented 7 years ago

@kamihouse Can you show me your configuration information? thanks!

lucastheisen commented 7 years ago

One more way to do this:

const entryMap = [
    ["polyfills", path.join(root, "src", "polyfills.ts")],
    ["vendor", path.join(root, "src", "vendor.ts")],
    ["app", path.join(root, "src", "index.ts")],
];
const entry = Object.assign({}, 
    ...entryMap.map(([key, value]) => ({[key]: value})));
const chunkSorter = (a, b) => {
    const order = Object.assign({}, 
        ...entryMap.map(([key, value], index) => ({[key]: index})));
    return order[a.names[0]] - order[b.names[0]];
}

module.exports = {
    entry,
    ...
    plugins: [
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: path.join(root, "src", "index.ejs"),
            chunksSortMode: chunkSorter,
        }),
    ]
    ...
}

Only real difference here is that the order is defined in the same place as the sorter, so you can't forget to update both locations when you add a new chunk...

szimek commented 7 years ago

Would it make sense to add a new options (e.g. called manual) that would keep the order exactly as defined in chunks array?

gregjacobs commented 7 years ago

Just ran into this issue myself. Would be nice to have the order of the chunks array be the order of the files in the html.

antony-oktana commented 7 years ago

Same issue over here. Would be nice a manual option that just adds them as the names are added in the array.

mb8z commented 7 years ago

Well, I am new to the opensource community, not really know how to add something from myself, but

  1. Edit the lib/chunksorter.js and replacing the module.exports.none so the none function looks like this:
    module.exports.none = function (chunks, originalChunksInput) {
    return chunks.sort((a, b) => originalChunksInput.indexOf(a.names[0]) - originalChunksInput.indexOf(b.names[0]));
    };
  2. Edit the lib/index.js file and update the HtmlWebpackPlugin.prototype.sortChunks function, so it accepts one more argument (originalChunksInput) and edit it so:
    // Sort mode auto by default:
    if (typeof sortMode === 'undefined') {
    sortMode = 'none';  // Change the default sortMode to none (if desired)
    }
    ...
    // Disabled sorting:
    if (sortMode === 'none') {
    return chunkSorter.none(chunks, originalChunksInput); // Pass the additional argument to the function
    }
  3. Look for the compiler.plugin('emit',... and update the code below // Sort chunks comment, so it passes the previously mentioned argument of originalChunksInput:
    // Sort chunks
    chunks = self.sortChunks(chunks, self.options.chunksSortMode, self.options.chunks);

Voila! This way you switched to the none as the default chunksSortMode option, and you provided a small change so the chunks are loaded in the way you put them into the chunks option.

Hope this helps somebody!

mrukas commented 7 years ago

An option to define the chunk order manually would be really great! +1

jantimon commented 7 years ago

Merged in #684

jantimon commented 7 years ago

Released 2.30.0

cleverboy32 commented 7 years ago

why i use chunksSortMode: manual or i write a function, all two no worker. and my version is the lastest. help ???

Javierb commented 7 years ago

In case you rather to set the order manually instead of creating a function and as per html-webpack-plugin 2.30.1 version. Setting chunksSortMode to 'manual' loads the chunks in the same order than in the chunks option.

// The following code will include vendor and app chunks in that particular order.
chunks: ['vendor', 'app'],
chunksSortMode: 'manual',
humorHan commented 7 years ago

When I met this problem, I gave the official feedback on why there was no attribute value of'manual', which would be more convenient. However, the official replied, "I can get the desired results in other ways, so...

But when I saw the new version of the problem, I was very happy, at least to accept the proposal

heisian commented 6 years ago

@Javierb is it just me or does your snippet not have any effect on the order of:

// Both are ordered as ['app', 'vendor', 'manifest']
compilation.getStats().toJson().chunks;
compilation.getStats().toJson().assetsByChunkName;

I am using:

new HtmlWebpackPlugin({
      inject: true,
      template: './index.html',
      chunks: ['manifest', 'vendor', 'app'], // doesn't seem to do anything?
      chunksSortMode: 'manual', // doesn't seem to do anything?
      ...
});

??

lock[bot] commented 6 years ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.