Skip to content

Question: Webpacker integration -> using local manifest.json for server-side rendering #739

Closed
@randyv12

Description

@randyv12

Hello!

I'm using the most recent version of react-rails (2.2.0) and Webpacker (1.2). One of the issues I ran into this week was server-side rendering, not fetching assets locally, but instead fetches from the remote asset host. I'm curious if this is fixed, if it's a bug (first of all), or if it will be-supported in the future or which gem will support it?

Explanation:

If Webpack detects that there is an asset_host set in a config, it will prepend the asset_host url in the manifest.json file in Webpacker (1.2) when it precompiles assets via rake webpacker:compile

webpacker/lib/install/config/webpack/configuration.js

const ifHasCDN = env.ASSET_HOST !== undefined && env.NODE_ENV === 'production'
const devServerUrl = `http://${devServer.host}:${devServer.port}/${paths.entry}/`
const publicUrl = ifHasCDN ? `${env.ASSET_HOST}/${paths.entry}/` : `/${paths.entry}/`
const publicPath = env.NODE_ENV !== 'production' ? devServerUrl : publicUrl

So when we precompile assets, our manifest.json file will contain, https://asset.host.com
and It will look like this

{file.js: 'https://assets.com/assets/file.js}

or like this

{file.js: '//assets.com/assets/file.js}

So when we server-side render using react ujs, using the web packer manifest container. The container will read from the local manifest.json with the asset host in it and recognize the asset_path that starts with 'http'. And when it does that, it will fetch the assets remotely, when server-side rendering. We don't want that, I think, we should just fetch it locally (since we have the files already)

react-rails/lib/react/server_rendering/webpack_manifest_container.rb

      def find_asset(logical_path)
        # raises if not found
        asset_path = Webpacker::Manifest.lookup(logical_path).to_s
        if asset_path.start_with?("http")
          # Get a file from the webpack-dev-server
          dev_server_asset = open(asset_path).read
          # Remove `webpack-dev-server/client/index.js` code which causes ExecJS to 💥
          dev_server_asset.sub!(CLIENT_REQUIRE, '//\0')
          dev_server_asset
        else
          # Read the already-compiled pack:
          full_path = Webpacker::Manifest.lookup_path(logical_path).to_s
          File.read(full_path)
        end
      end

Current Solution

To get around that and solve this issue we will have to create a separate manifest.json file (from Webpack, not Webpacker), which will not include the asset host, so we can locally reference them, without fetching assets remotely.

That, we can do on the Webpacker gem side and add in a separate webpack manifest plugin entry that creates a separate manifest.json for server-side rendering in a separate folder, something like:

webpacker/lib/install/config/webpack/shared.js

  plugins: [
    new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
    new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
    new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true }),
    new ManifestPlugin({
      fileName: '../pre-render/manifest.json',
      publicPath: `${paths.prerender_path}/`,
      writeToFileEmit: true})
  ],

Then, after that, we will need to read that server-side manifest json file in
react-rails/lib/react/server_rendering/webpack_manifest_container.rb
so we can use the local assets that are precompiled, for server-side rendering

something like this:

module React
  module ServerRendering
    class WebpackerManifestContainer
      def find_asset(logical_path)
        path = ::Rails.root.join(File.join(Webpacker::Configuration.output_path, '/pre-render/manifest.json'))
        full_path = ::Rails.root.join(File.join(Webpacker::Configuration.output_path, JSON.parse(File.read(path))[logical_path.to_s]))

        if full_path
          return File.read(full_path)
        end

       // else do original find_asset stuff
    end
  end
end

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions