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

When emitting runtimeChunk, entry chunks use `chunkFilename` instead of `filename` #6598

Open
nirazul opened this issue Feb 27, 2018 · 21 comments

Comments

@nirazul
Copy link

commented Feb 27, 2018

Do you want to request a feature or report a bug?
Bug (?)

What is the current behavior?
When defining options.runtimeChunk with true or another value, my entry modules are being written with the chunkFilename option instead of the filename option. However, the runtime doesn't seem to load my entry modules. When I add a script tag for the entry file, the page loads normally.

Which modules do I need to write script tags for? I'm automatically injecting all files that are in the directory controlled by the filename property.

If the current behavior is a bug, please provide the steps to reproduce.

const config = {
    entry: {
        app: [
            path.join(myCwd, 'app/main/index.js'),
            path.join(myCwd, 'app/main/hmr.js'),
        ],
    },
    mode: isDist ? 'production' : 'development',
    optimization: {
        namedModules: true,
        noEmitOnErrors: !isDist,
        runtimeChunk: true,
        splitChunks: false,
    },
    output: {
        path: destPath,
        filename: 'entry/[name]/index.js',
        chunkFilename: 'chunks/[name]/index.[chunkhash].js',
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
    ],
    resolve: {
        modules: [
            'node_modules',
            path.resolve(myCwd, 'app'),
        ],
    },
}

What is the expected behavior?
Entry chunks are emitted using the filename property.

Please mention other relevant information such as the browser version, Node.js version, webpack version, and Operating System.

  • webpack v4.0.1
  • node v8.9
@sokra

This comment has been minimized.

Copy link
Member

commented Feb 27, 2018

The Stats has a entrypoints property which contains information about which files you need to add script tags for.

@nirazul

This comment has been minimized.

Copy link
Author

commented Feb 27, 2018

@sokra
Thanks for your answer. I've tried it out and see that one entrypoint is the runtime and the other one is a chunk:

Entrypoint app = entry/runtime~app/index.js entry/runtime~app/index.js.map chunks/app/index.49dde4511aa9907b6bef.js chunks/app/index.49dde4511aa9907b6bef.js.map

Is there any way to programmatically separate the entry files from the chunks?
I need to implement two differen caching strategies (serverside for script tags, and webpack hashes for chunks). For automatic script tag injection, I need to know after compilation, which files I need to add. In webpack 3 I could separate them by folder name.

@sokra

This comment has been minimized.

Copy link
Member

commented Feb 27, 2018

I need to implement two differen caching strategies

try to always use hashes. filename: "[chunkhash].js"

For automatic script tag injection, I need to know after compilation, which files I need to add.

see entrypoints property and include all js files in the list. You don't need folders

Is there any way to programmatically separate the entry files from the chunks?

All chunks are equal, technically a chunk used at entry time could also used on-demand for another entry. Only the runtime chunks is different, because it includes the runtime.

@nirazul

This comment has been minimized.

Copy link
Author

commented Feb 27, 2018

@sokra
Thanks for your help, I really appreciate it!
I have a bit of a complicated setup. Unfortunately, when I'm using a chunkhash, the filenames would change every time I edit the file. This would lead to me checking in an ever changing HTML file to our git.

So I guess the way forward for me is to consume the entrypoints property. Unfortunately, I can't directly implement a chunk with a chunkhash attached due to the above mentioned redundant git commits.

I don't see any usable upgrade path for us, except disabling the runtimeChunk :/

@davidmerrique

This comment has been minimized.

Copy link

commented Feb 27, 2018

@nirazul I'm also running into this, with a similar config to yours.

If I omit the output.chunkFilename option, entry chunks properly use filename.
However I then can't customize chunkFilename.

@sokra I think this is a bug, using output.chunkFilename shouldn't change output.filename.

Another thing I noticed, including runtimeChunk: 'single' also breaks output.filename

Take a look at these outputs:

/**
 * This is what I want:
 * runtime.js
 * pages/home.js
 * pages/contact.js
 * chunks/0.591536ba90b5d040ff6c.js
 * chunks/...
 * chunks/...
 */

const config = {
  entry: {
    'pages/home': 'app/pages/home.js',
    'pages/contact': 'app/pages/contact.js'
  },
  output: {
    filename: '[name].js',
    chunkFilename: 'chunks/[id].[chunkhash].js'
  },
  optimization: {
    runtimeChunk: 'single'
  }
};
/**
 * Output:
 * runtime.js
 * /pages/home.db984bf047928e400396.js <- entry points are not using output.filename properly
 * /pages/contact.c1abdbedcfe99819cca7.js
 * 0.591536ba90b5d040ff6c.js <- chunks are not in chunks/ folder
 * 1.ac4d619bda6ddfd7c91a.js
 */

const config = {
  entry: {
    'pages/home': 'app/pages/home.js',
    'pages/contact': 'app/pages/contact.js'
  },
  output: {
    filename: '[name].js',
    // chunkFilename: 'chunks/[id].[chunkhash].js' <- Remove this option
  },
  optimization: {
    runtimeChunk: 'single'
  }
};
/**
 * Output:
 * runtime.js
 * /pages/home.js <- this is correct now
 * /pages/contact.js
 * 0.js <- I can't use output.chunkFilename
 * 1.js
 */

const config = {
  entry: {
    'pages/home': 'app/pages/home.js',
    'pages/contact': 'app/pages/contact.js'
  },
  output: {
    filename: '[name].js',
    chunkFilename: 'chunks/[id].[chunkhash].js'
  },
  // optimization: {
  //   runtimeChunk: 'single' <- Remove this option
  // }
};
/**
 * Output:
 * ______ <- I want a single runtime chunk
 * /pages/home.js <- this is correct
 * /pages/contact.js
 * 0.591536ba90b5d040ff6c.js <- This is wrong, should be in chunks/ folder
 * 1.ac4d619bda6ddfd7c91a.js
 */

@sokra sokra added this to the webpack 4 milestone Feb 28, 2018

@jconroy

This comment has been minimized.

Copy link

commented Mar 1, 2018

I think I too and hitting a similar issues https://gist.github.com/jconroy/b8f5e31ddfc040faf69a37fad3acb065

sokra added a commit that referenced this issue Mar 2, 2018

@glen-84

This comment has been minimized.

Copy link

commented Apr 11, 2018

I've also experienced the issues described by @davidmerrique, and there is another related issue:

I'm getting this error:

ERROR in chunk scripts/runtime [entry]
[name]-[chunkhash].js
Cannot use [chunkhash] or [contenthash] for chunk in '[name]-[chunkhash].js' (use [hash] instead)

There's no option in optimization.runtimeChunk to set the file name, so it's using output.filename, which includes [chunkhash]. The only way to avoid this is to remove the [chunkhash] from output.filename, which I don't want to do.

@sokra These issues seem to be quite serious – will you be able to look into them soon?

@volodalexey

This comment has been minimized.

Copy link

commented Apr 18, 2018

runtimeChunk name works for me (strange combination + filename) webpack 4.5.0:

module.exports = {
  ...
  output: {
    filename: '[name].bundle.js',
    chunkFilename: '[name].chunk.js',
    ...
  },
  optimization: {
    runtimeChunk: {
      name: 'manifest'
    }
    ...
  },
  ...
}

Output in my case:

      app.chunk.js   27.4 KiB       app  [emitted]  app
manifest.bundle.js   4.93 KiB  manifest  [emitted]  manifest
   vendor.chunk.js   2.95 MiB    vendor  [emitted]  vendor
@nirazul

This comment has been minimized.

Copy link
Author

commented Apr 18, 2018

@volodalexey The issue isn't the runtimeChunk generation, but using it together with chunkhash

@MhMadHamster

This comment has been minimized.

Copy link
Contributor

commented Apr 22, 2018

I've made a small plugin which allows you to rename chunks, or use your output.filename option for initial chunks with entry and without runtime. Should help with this issue: npm link.

@nirazul

This comment has been minimized.

Copy link
Author

commented Apr 23, 2018

@MhMadHamster
I've tried out your plugin and it works like a charm! Thank you very much!

Would be nice to introduce the functionality into wepback core as the documentation states that by default, entry chunks should use the filename template.

@flibustier7seas

This comment has been minimized.

Copy link

commented Jun 1, 2018

@glen-84
I was also getting this error:

ERROR in chunk runtime [entry]
[name].[chunkhash].js
Cannot use [chunkhash] or [contenthash] for chunk in '[name].[chunkhash].js' (use [hash] instead)

I think it has something to do with HotModuleReplacementPlugin, because without It everything works well.

Solution for me - overload output configuration for dev build:

    output: {
        path: path.join(__dirname, "dist"),
        filename: "[name].js",
    },

@sokra sokra modified the milestones: webpack 4, webpack 4.x Jun 21, 2018

@glen-84

This comment has been minimized.

Copy link

commented Jun 23, 2018

@flibustier7seas Ya, I was doing something like: filename: (isDev ? "[name].js" : "[name]-[chunkhash].js").

Edit: Now using filename: (isHot ? "[name].js" : "[name]-[contenthash:8].js").

@timneutkens

This comment has been minimized.

Copy link
Contributor

commented Jun 27, 2018

Looks like @SevInf is working on a fix 🎉

We're running into this issue with Next.js, as entries don't have chunk hashes right now, but dynamic imports do.

@glen-84

This comment has been minimized.

Copy link

commented Jun 27, 2018

@timneutkens The fix will only be in webpack 5 from what I can tell ... #7401.

@timneutkens

This comment has been minimized.

Copy link
Contributor

commented Jun 29, 2018

As we needed this for Next.js I spent some time writing a plugin that applies the fix @SevInf wrote:

export default class ChunkNamesPlugin {
  apply (compiler) {
    compiler.hooks.compilation.tap('NextJsChunkNamesPlugin', (compilation) => {
      compilation.chunkTemplate.hooks.renderManifest.intercept({
        register(tapInfo) {
          if(tapInfo.name === 'JavascriptModulesPlugin') {
            const originalMethod = tapInfo.fn
            tapInfo.fn = (result, options) => {
              let filenameTemplate;
              const chunk = options.chunk;
              const outputOptions = options.outputOptions;
              if (chunk.filenameTemplate) {
                filenameTemplate = chunk.filenameTemplate;
              } else if (chunk.hasEntryModule()) {
                filenameTemplate = outputOptions.filename;
              } else {
                filenameTemplate = outputOptions.chunkFilename;
              }

              options.chunk.filenameTemplate = filenameTemplate
              return originalMethod(result, options)
            }
          }
          return tapInfo
        }
      })
    })
  }
}
@AlexMost

This comment has been minimized.

Copy link

commented Oct 5, 2018

Is there any progress on this ticket? I have also met this trouble. if I set runtimeChunk, the chunkFilename format is applied instead of the filename.

@vv314

This comment has been minimized.

Copy link

commented Oct 25, 2018

So, will this be fixed in webpack 4.x?

@hisapy

This comment has been minimized.

Copy link

commented Dec 20, 2018

I modified @timneutkens's example to add support for .css initial chunks.

class ChunkNamesPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap('ChunkNamesPlugin', compilation => {
      compilation.chunkTemplate.hooks.renderManifest.intercept({
        register(tapInfo) {
          if (tapInfo.name === 'JavascriptModulesPlugin') {
            const originalFn = tapInfo.fn;

            tapInfo.fn = (result, options) => {
              const chunkName = options.chunk.name;

              // Don't mutate options passed to other plugins
              let customOpts = { ...options };

              if (chunkName === 'main' || chunkName === 'vendors~main') {
                customOpts.outputOptions = {
                  ...options.outputOptions,
                  chunkFilename: 'js/[name].chunk.js',
                };

                const hasCss =
                  result[0] &&
                  result[0].identifier.startsWith('mini-css-extract-plugin');

                if (hasCss) {
                  result[0].filenameTemplate = 'css/[name].chunk.css';
                }
              }

              originalFn(result, customOpts);
            };
          }

          return tapInfo;
        },
      });
    });
  }
}

Actually, I'm using it in our react-scripts fork

@danielkoch

This comment has been minimized.

Copy link

commented Jan 11, 2019

Hi @hisapy, thanks for that solution. Did you used this plugin in combination with optimization.runtimeChunk = true and hashed filenames? Looks like the new names for css files are not populated into the runtime file.

@hisapy

This comment has been minimized.

Copy link

commented Jan 11, 2019

According to the webpack.config for CRA, our runtimeChunk is true https://github.com/iporaitech/create-react-app/blob/iporaitech-react-scripts/packages/react-scripts/config/webpack.config.js#L254

However, with our custom build we don't add hashed file names of the initial chunks. We just add them to dynamic chunks. The hashes and manifest for initial chunks, the ones that we put in script and link tags in the HTML are generated by our Elixir Phoenix web application.

I believe the fork is out-of-date though. We haven't received PRs from Backstroke, the service that PRs each time master in upstream is updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.