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

MissingEntryError with javascript_packs_with_chunks_tag but not with javascript_pack_tag #1967

Closed
robbymarston opened this issue Feb 28, 2019 · 9 comments

Comments

@robbymarston
Copy link

robbymarston commented Feb 28, 2019

I'm getting a MissingEntryError when I use javascript_packs_with_chunks_tag but not with javascript_pack_tag. Here's the aforementioned error:

Rescued toplevel: Webpacker can't find react-manager in /my_app/public/packs/manifest.json.
...
Your manifest contains:
{
  "react-manager.css": "/packs/react-manager-66702cf30ae4ffc6025f.css",
  "react-manager.js": "/packs/react-manager-66702cf30ae4ffc6025f.js",
  "vendors~react-manager.js": "/packs/vendors~react-manager-eb2e5fd06be7538f6120.js",
 ...
}

Clearly react_manager is present in the manifest and in the packs directory, so I'm not sure what the issue is. Note: I'm using Webpacker 4.0.0.rc.7 with Webpack 4.29.5. and react_on_rails' recommended project structure.

Here are my source files (edited for readability):

config/webpacker.yml

default: &default
  cache_path: tmp/cache/webpacker
  compile: false
  public_output_path: packs
  source_path: client
development:
  <<: *default
test:
  <<: *default
  public_output_path: packs-test
staging: &staging
  <<: *default
  cache_manifest: true
production:
  <<: *staging

webpack.config.js

const ManifestPlugin = require('webpack-manifest-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { resolve } = require('path');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const webpackConfigLoader = require('react-on-rails/webpackConfigLoader');

const configPath = resolve('..', 'config');
const { output } = webpackConfigLoader(configPath);

module.exports = (env, args) => ({
  context: resolve(__dirname, 'app'),
  devtool: args.mode === 'development' ? 'source-map' : false,
  entry: {
    'react-manager': [
      './bundles/manager/startup/managerRegistration',
    ],
  },
  output: {
    filename: '[name]-[chunkhash].js',
    path: output.path,
    publicPath: output.publicPath,
  },
  resolve: {
    extensions: ['.js', '.jsx', '.scss'],
    modules: [
      'client/app',
      'client/node_modules',
    ],
  },
  module: {
    rules: [
      {
        exclude: resolve(__dirname, 'node_modules'),
        test: /\.jsx?$/,
        use: 'babel-loader',
      },
      {
        exclude: resolve(__dirname, 'node_modules'),
        test: /\.scss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: 'css-loader',
            options: {
              localIdentName: '[local]___[hash:base64:5]',
              modules: true,
              sourceMap: args.mode === 'development',
            },
          },
          'sass-loader',
        ],
      },
    ],
  },
  optimization: {
    runtimeChunk: {
      name: 'manifest',
    },
    splitChunks: {
      chunks: 'all',
    }, 
    minimizer: [
      new UglifyJsPlugin({
        sourceMap: args.mode === 'development',
      }),
      new OptimizeCSSAssetsPlugin({}),
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name]-[chunkhash].css',
    }),
    new ManifestPlugin({
      publicPath: output.publicPath,
    }),
  ],
});

index.html.erb

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My App</title>
    <%= stylesheet_packs_with_chunks_tag "react-manager" %>
  </head>
  <body>
    <%= javascript_packs_with_chunks_tag 'react-manager' %>
  </body>
</html>

Any help is much appreciated, thanks!

@robbymarston
Copy link
Author

UPDATE:

It looks like Webpacker is expecting this JSON schema for manifest.json:

{
  "entrypoints": {
    "js": {
      "react-manager": "/packs/react-manager-66702cf30ae4ffc6025f.js"
    }
  }
}

I'm not positive, but is that the CommonsChunk JSON schema?

Wepacker says it supports Webpack v4; however, the Webpack v4 documentation recommends using SplitChunksPlugin in combination with the ManifestPlugin (see here). The issue is, the ManifestPlugin doesn't output the manifest in the required schema, it uses the standard schema that javascript_pack_tag expects.

@bstst
Copy link

bstst commented May 9, 2019

There's another manifest plugin, https://github.com/webdeveric/webpack-assets-manifest, it supports an option entrypoints (https://github.com/webdeveric/webpack-assets-manifest#entrypoints), which compiles a manifest with the entrypoints schema.

@h0jeZvgoxFepBQ2C
Copy link

h0jeZvgoxFepBQ2C commented May 13, 2019

Can confirm, I have exactly the same issue 👍
Did you find any workaround?

@jakeNiemiec
Copy link
Member

@robbymarston @h0jeZvgoxFepBQ2C Can you post the entire error log with the entire manifest.json file?

@h0jeZvgoxFepBQ2C
Copy link

h0jeZvgoxFepBQ2C commented May 19, 2019

Looks like this is working for me:

in webpack environment.js

const WebpackAssetsManifest = require('webpack-assets-manifest');
environment.plugins.insert(
  'Manifest',
  new WebpackAssetsManifest({
    entrypoints: true,
    writeToDisk: true,
    publicPath: true,
    done: function(manifest, stats) {
      console.log(`The manifest has been written to ${manifest.getOutputPath()}`);
      console.log(`${manifest}`);
    }
  })
)

and then use

# NOT WORKING WITH JS ENDING!
=javascript_packs_with_chunks_tag "quick_add/index.js"

# WORKS JS ENDING!!!!
=javascript_packs_with_chunks_tag "quick_add/index"

Strange thing is now, that it doesnt work anymore with javascript_include_tag :( It outputs a include tag, but somehow the JS is not loaded.. Dunno why.

@h0jeZvgoxFepBQ2C
Copy link

h0jeZvgoxFepBQ2C commented May 19, 2019

Ok this f... still not working, since my chunks are loaded twice :( When I only use javascript_pack_tag it renders the javascript include tag, but the JS is not executed somehow, not even a console.log("Hi") at the top of my component1 file.

Can somone post an example on how to do following basic stuff?

# application_layout
=javascript_packs_with_chunks_tag "application"
# includes polyfill, moment and some other basic libraries

# subsite 1
=javascript_pack_tag "react_component1"
# imports react,axios and react_component1

# subsite 1
=javascript_pack_tag "react_component2"
# imports react,axios and react_component2

And with splitchunks each npm module should be in its own file (https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758?gi=3cd6ba7651c0)
React, Axios,.. is imported in each react component, but should (logically) only loaded once?

I just can't make it work :( This should be basic stuff and not take 30+ hours to finish :( I miss the old times where I could just use sprockets and just require these things easily.

@jakeNiemiec
Copy link
Member

jakeNiemiec commented May 20, 2019

@h0jeZvgoxFepBQ2C, let me repurpose my comment from here #1835 (comment):

Splitting your app into multiple packs, just to load them on the same page is the waste of performance and compile time. Webpack is assuming that those different entrypoints are on exclusive pages (hence the duplication).

Example: <%= javascript_packs_with_chunks_tag 'application', 'react_component1', 'react_component2' %>

This would be the proper way to make a tree-shaken entrypoint:

<%
  # Some erb that loads in <head>.
  #  We need a way to get rails/erb data into webpack-land, 
  #  the best way is to just have a "flag" for each chunk you might want to load.
%>
<script>
  window.lazyChunks = {
    react_component1: <%= @lazy_chunks[: react_component1].present? %>,
    react_component2: <%= @lazy_chunks[: react_component2].present? %>,
  };
</script> 
<%= javascript_packs_with_chunks_tag 'application' %>
<% # Some partial that contains your react_component1 component %>
<% # Setting this here will make `window.lazyChunks.react_component1` true in webpack-land %>
<%= @lazy_chunks[:react_component1] = true %>
<div id='react_component1'></div>
// application.js
if(window.lazyChunks.react_component1)               // resolves to true from the other file
  import(/* webpackMode: "lazy" */                   // can be 'lazy', 'lazy-once', 'eager', or 'weak'
         /* webpackChunkName: "react_component1" */  // Adding this comment will cause our separate chunk to be named [my-chunk-name].js instead of [id].js.
         './../file/path/to/react_component1');

if(window.lazyChunks.react_component2)
  import(/* webpackMode: "lazy" */
         /* webpackChunkName: "react_component2" */
         './../file/path/to/react_component2');

Docs for using ES6 dynamic import().
Docs for using ES6 dynamic import for webpack.
Edit: This is not new, it has been a standard feature since 2017. IMHO, it is not used nearly enough.

Any sub-files that you need to load will be available to this single entrypoint. Without this, you are fighting and negating any tree-shaking. Better yet, but using dynamic import, webpack can automatically generate vendor chunks (like React) that are shared between your lazy chunks!

These are the same methods that your linked guide uses. You can further customize this in webpackER via splitChunks, here is the doc: https://github.com/rails/webpacker/blob/master/docs/webpack.md#add-splitchunks-webpack-v4

I just can't make it work :( This should be basic stuff and not take 30+ hours to finish :( I miss the old times where I could just use sprockets and just require these things easily.

Like I said in the other issue, WebpackER makes poor use of webpacks strengths. You are one of many having similar issues. Feel free to ask any follow-up questions, hope this helps!

@h0jeZvgoxFepBQ2C
Copy link

Actually I can't really tell why I even have to use webpacker.. Maybe it makes more sense to use just webpack and remove the webpacker gem?

I think i understand your solution, but it still looks kinda dirty hack somehow :/ And actually the compiling performance is much better, since during changes you only have to compile your own code, not compile the 1MB file in total with all npm packages included.. At least this is the reason why I even do this - and which also happens now (-> compiling is pretty fast now).

@jakeNiemiec
Copy link
Member

jakeNiemiec commented May 21, 2019

And actually the compiling performance is much better, since during changes you only have to compile your own code, not compile the 1MB file in total with all npm packages included...(-> compiling is pretty fast now).

I and others have suggested this change, it's a shame to see webpack get such a bad rap from webpackER.

Actually I can't really tell why I even have to use webpacker...Maybe it makes more sense to use just webpack and remove the webpacker gem?

You don't, if you look around at other issues, I give better details on how you can resolve packs yourself from a manifest.json. Here is one such comment: #1903 (comment).

image
Think of removing training wheels from a bike, you can now take tighter turns at the cost of stability. In a similar way, it isn't webpackERs fault, it needs to be a zero-config tool for many different frameworks; the cost of that is speed & customization. This is why most JS frameworks include an "I know what I am doing" eject feature. Best of luck in finding the path that suits you best.

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

5 participants