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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
     blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    
    Skip to content

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

    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
    

    Activity

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
      Skip to content

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

      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
      

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Assignees

      Type

      No type

      Projects

      No projects

        Milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
        Skip to content

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

        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
        

        Activity

        Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

        Metadata

        Metadata

        Assignees

        Type

        No type

        Projects

        No projects

          Milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
          Skip to content

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

          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
          

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Assignees

          Type

          No type

          Projects

          No projects

            Milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
            Skip to content

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

            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
            

            Activity

            Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

            Metadata

            Metadata

            Assignees

            Type

            No type

            Projects

            No projects

              Milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

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

              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
              

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Assignees

              Type

              No type

              Projects

              No projects

                Milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                Skip to content

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

                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
                

                Activity

                Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                Metadata

                Metadata

                Assignees

                Type

                No type

                Projects

                No projects

                  Milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions