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

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

Author: Fantashit

7 thoughts on “When emitting runtimeChunk, entry chunks use `chunkFilename` instead of `filename`

  1. @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
     */
  2. 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?

  3. 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.

  4. @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.

  5. @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").

  6. 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
            }
          })
        })
      }
    }

Comments are closed.