Mini-css-extract-plugin: "Extracting CSS based on entry" example doesn't work

Created on 26 Jul 2018  Â·  10Comments  Â·  Source: webpack-contrib/mini-css-extract-plugin

Hello!
I found that the condition in a cacheGroups is never true:

test: (m,c,entry = 'foo') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,

because module object in the recursiveIssuer function doesn't have a name field

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else if (m.name) { // <----- always undefined
    return m.name;
  } else {
    return false;
  }
}

webpack: 4.16.2
plugin: 0.4.1

good first pr help wanted 3 (important) 3 (broken) docs

Most helpful comment

hi bro, i also do the same thing you do , but i have find a error that Cannot read property 'pop' of undefined

All 10 comments

I also had this issue - I logged the object (m) and found the name in this part:

_chunks: 
   SortableSet {
     Chunk {
     id: null,
     ids: null,
     debugId: 1035,
     name: 'base', // <----This is my entry point name
     preventIntegration: false,
     entryModule: [Circular],
     _modules: [Object],
     filenameTemplate: undefined,
     _groups: [Object],
     files: [],
     rendered: false,
     hash: undefined,
     contentHash: {},
     renderedHash: undefined,
     chunkReason: undefined,
     extraAsync: false,
     removedModules: undefined },
     _sortFn: [Function: sortById],
     _lastActiveSortFn: null,
     _cache: undefined,
     _cacheOrderIndependent: undefined },

So I modified the function to this:

function recursiveIssuer(m) {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else {
    for (var chunk of m._chunks) {
      return chunk["name"]
    }
    return false;
  }
}

I'm not sure this is the correct way since it requires accessing a private object but I couldn't figure out another way

hi bro, i also do the same thing you do , but i have find a error that Cannot read property 'pop' of undefined

I also had this problem. The solution @melissa-hong suggested did not work for me exactly as written, but it did lead me in the right direction, so thanks for that! I think the problem I had with the stated solution above involved using a for ... of loop with the SortableSet, m._chunks.

A colleague, @hieushift, and I came up with two different solutions that both seem to solve the problem, so hopefully they are helpful to others. I am curious, though, if anyone with more experience with this code could comment on whether or not either of these two work-arounds are particularly good or bad?

Approach 1 (more similar to approach suggested by docs):

UPDATE: This first approach ended up not working. I saw that a css file generated for a different entrypoint was being fetched in the wrong app, and also observed some CSS regressions

Assume app entry point is named app.

function recursiveIssuer(m): {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else {
    return Array.from(m._chunks)[0].name;
  }
}

optimization.splitChunks.cacheGroups: {
  'app-styles': {
    name: 'app-styles',
    test: (m, c, entry = 'app') => {
      return m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry;
    },
    chunks: 'all',
    enforce: true,
  }
}

Approach 2:

No recursive issuer in this approach. Let's assume the name of the entry point is app (same assumption as above).

optimization.splitChunks.cacheGroups: {
  'app-styles': {
    name: 'app-styles',
    test: module => module.constructor.name === 'CssModule',
    chunks: chunk => chunk.name.startsWith('app'),
    enforce: true,
  }
}

I lean towards thinking approach 1 above is slightly better since we arrive directly at the entry point name for an exact equality check, but still curious what others may think.

Note:

We use HTMLWebpackPlugin v3.2.0, and do not automatically inject assets into the html template. For both solutions above, in order to make the generated css chunk available as part of htmlWebpackPlugin.files.css in the plugin, I had to add app-styles to the array of chunks in the HtmlWebpackPlugin config. Perhaps this should be expected, but this also caused an additional Javascript chunk to be injected into the page with the following file contents:

(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);

I'm not too sure how important or unimportant this small chunk is, so any insight on that would be helpful. The HtmlWebpackPlugin inserted this chunk as a script tag above the inlined manifest, which doesn't seem correct, but also doesn't seem to cause problems.

I also had this problem. The solution @melissa-hong suggested did not work for me exactly as written, but it did lead me in the right direction, so thanks for that! I think the problem I had with the stated solution above involved using a for ... of loop with the SortableSet, m._chunks.

A colleague, @hieushift, and I came up with two different solutions that both seem to solve the problem, so hopefully they are helpful to others. I am curious, though, if anyone with more experience with this code could comment on whether or not either of these two work-arounds are particularly good or bad?

Approach 1 (more similar to approach suggested by docs):

UPDATE: This first approach ended up not working. I saw that a css file generated for a different entrypoint was being fetched in the wrong app, and also observed some CSS regressions

Assume app entry point is named app.

function recursiveIssuer(m): {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else {
    return Array.from(m._chunks)[0].name;
  }
}

optimization.splitChunks.cacheGroups: {
  'app-styles': {
    name: 'app-styles',
    test: (m, c, entry = 'app') => {
      return m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry;
    },
    chunks: 'all',
    enforce: true,
  }
}

Approach 2:

No recursive issuer in this approach. Let's assume the name of the entry point is app (same assumption as above).

optimization.splitChunks.cacheGroups: {
  'app-styles': {
    name: 'app-styles',
    test: module => module.constructor.name === 'CssModule',
    chunks: chunk => chunk.name.startsWith('app'),
    enforce: true,
  }
}

I lean towards thinking approach 1 above is slightly better since we arrive directly at the entry point name for an exact equality check, but still curious what others may think.

Note:

We use HTMLWebpackPlugin v3.2.0, and do not automatically inject assets into the html template. For both solutions above, in order to make the generated css chunk available as part of htmlWebpackPlugin.files.css in the plugin, I had to add app-styles to the array of chunks in the HtmlWebpackPlugin config. Perhaps this should be expected, but this also caused an additional Javascript chunk to be injected into the page with the following file contents:

(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);

I'm not too sure how important or unimportant this small chunk is, so any insight on that would be helpful. The HtmlWebpackPlugin inserted this chunk as a script tag above the inlined manifest, which doesn't seem correct, but also doesn't seem to cause problems.

I also had this problem. The solution @melissa-hong suggested did not work for me exactly as written, but it did lead me in the right direction, so thanks for that! I think the problem I had with the stated solution above involved using a for ... of loop with the SortableSet, m._chunks.

A colleague, @hieushift, and I came up with two different solutions that both seem to solve the problem, so hopefully they are helpful to others. I am curious, though, if anyone with more experience with this code could comment on whether or not either of these two work-arounds are particularly good or bad?

Approach 1 (more similar to approach suggested by docs):

UPDATE: This first approach ended up not working. I saw that a css file generated for a different entrypoint was being fetched in the wrong app, and also observed some CSS regressions

Assume app entry point is named app.

function recursiveIssuer(m): {
  if (m.issuer) {
    return recursiveIssuer(m.issuer);
  } else {
    return Array.from(m._chunks)[0].name;
  }
}

optimization.splitChunks.cacheGroups: {
  'app-styles': {
    name: 'app-styles',
    test: (m, c, entry = 'app') => {
      return m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry;
    },
    chunks: 'all',
    enforce: true,
  }
}

Approach 2:

No recursive issuer in this approach. Let's assume the name of the entry point is app (same assumption as above).

optimization.splitChunks.cacheGroups: {
  'app-styles': {
    name: 'app-styles',
    test: module => module.constructor.name === 'CssModule',
    chunks: chunk => chunk.name.startsWith('app'),
    enforce: true,
  }
}

I lean towards thinking approach 1 above is slightly better since we arrive directly at the entry point name for an exact equality check, but still curious what others may think.

Note:

We use HTMLWebpackPlugin v3.2.0, and do not automatically inject assets into the html template. For both solutions above, in order to make the generated css chunk available as part of htmlWebpackPlugin.files.css in the plugin, I had to add app-styles to the array of chunks in the HtmlWebpackPlugin config. Perhaps this should be expected, but this also caused an additional Javascript chunk to be injected into the page with the following file contents:

(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);

I'm not too sure how important or unimportant this small chunk is, so any insight on that would be helpful. The HtmlWebpackPlugin inserted this chunk as a script tag above the inlined manifest, which doesn't seem correct, but also doesn't seem to cause problems.

hello , Approach 2, if app-styles named app, it will cause a error that cannot read property 'pop' of undefined. why? The mini-css-extract-plugin has a bug ?

It is still issue for me.

/cc @beshanoe problem still exists?

@evilebottnawi I didn't check yet, but I think so. Even though @njgraf512 suggested some ways to solve it, looks like there's no solid solution yet. And I guess the most confusion is caused by the fact that nobody knows which field of module object to use in order to reliably identify an entrypoint. Cause it's pretty internal to webpack.
Maybe it would be better to move towards a more user-friendly approach and hide implementation details, e.g. generate cacheGroups inside the plugin

@beshanoe thanks!

Somebody can create minimum reproducible test repo, it is allow to identify what is wrong and how better fix it?

UPDATE:
I WAS ABLE to fix the issue creating another cacheGroup for that particular part (previously)

Same issue. Can't create minimum reproducible test repo. Continure investigation

    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '&&',
      name: true,
      cacheGroups: {
// =============== THIS PART MADE IT WORK =========== 
        ['weplay-components']: {
          priority: 2,
          test: module => module.context && module.context.includes(name),
          chunks: 'all',
          maxInitialRequests: Infinity,
          maxSize: Infinity,
          minSize: 0,
          name: `npm/custom/weplay-components`,
        },
// =============== END: THIS PART MADE IT WORK =========== 
        mainNodeModule: {
          priority: 1,
          test: /[\\/]node_modules[\\/]/,
          chunks: 'initial',
          maxInitialRequests: Infinity,
          minSize: 10 * 1000,
          name: 'npm/main-sync-chunk',
        },
        asyncNodeModules: {
          priority: 1,
          test: /[\\/]node_modules[\\/]/,
          chunks: 'async',
          maxInitialRequests: Infinity,
          minSize: 1000,
          name(module) {
            const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]

            return `npm/async/${packageName.replace('@', '')}`
          },
        },
}

issue
ERROR in chunk npm/async/weplay-components css/npm/async/weplay-components.css Cannot read property 'pop' of undefined
so basically it just doesn't move out weplay-components.css, it leaves it in npm/main-sync-chunk

Closing due to inactivity. If somebody have problem with example please open new issue with reproducible example, thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

YanYuanFE picture YanYuanFE  Â·  3Comments

skrobek picture skrobek  Â·  4Comments

dstarosta picture dstarosta  Â·  3Comments

elgs picture elgs  Â·  3Comments

dmitrybelyakov picture dmitrybelyakov  Â·  3Comments