'Next.js and Webpack inline Javascript or CSS ressources

I have the following use case: I need to generate static files with React and Next.js. For that I extended the next.config.js with the following content:

module.exports = {
  exportTrailingSlash: true,
  exportPathMap: async function () {
    const paths = {
      '/': { page: '/' }
    }
    const errorPages = [
      { id: 1, name: '404', lang: 'en' },
      { id: 2, name: '500', lang: 'en' }
    ]

    errorPages.forEach(errorPage => {
      paths[`/errorPage/${errorPage.id}`] = {
        page: '/errorPage/[id]',
        query: { id: errorPage.id }
      }
    })

    return paths
  },
}

This totally works. The files are beeing generated in the out folder after npm export

An additional use case however is this one: The index.html file needs to work everywhere, not just in the exact place it has in the out folder. For example another webapp queries an API which scans the index.html and returns the html as string from that file. This html string has to work, but the references for the js files/bundles are relative (see link and script tags):

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width,minimum-scale=1,initial-scale=1"
    />
    <meta name="next-head-count" content="2" />
    <link
      rel="preload"
      href="/_next/static/UkokJ2QynwMCza2ya8Vuz/pages/index.js"
      as="script"
    />
    <link
      rel="preload"
      href="/_next/static/UkokJ2QynwMCza2ya8Vuz/pages/_app.js"
      as="script"
    />
    <link
      rel="preload"
      href="/_next/static/runtime/webpack-91b117697e716c22a78b.js"
      as="script"
    />
    <link
      rel="preload"
      href="/_next/static/chunks/framework.94bc9fd9a7de53a90996.js"
      as="script"
    />
    <link
      rel="preload"
      href="/_next/static/chunks/commons.6419ced117edf62014da.js"
      as="script"
    />
    <link
      rel="preload"
      href="/_next/static/runtime/main-2ba45c6c0e61dfa5f796.js"
      as="script"
    />
  </head>
  <body>
    <div id="__next">
      <div style="margin:20px;padding:20px;border:1px solid #DDD">
        <h1>Error Pages</h1>
        <ul>
          <li><a href="/errorPage/1/">test a</a></li>
          <li><a href="/errorPage/2/">test 2</a></li>
        </ul>
      </div>
    </div>    
    <script
      nomodule=""
      src="/_next/static/runtime/polyfills-2889d9d9fcf08314dd3a.js"
    ></script>
    <script
      async=""
      data-next-page="/"
      src="/_next/static/UkokJ2QynwMCza2ya8Vuz/pages/index.js"
    ></script>
    <script
      async=""
      data-next-page="/_app"
      src="/_next/static/UkokJ2QynwMCza2ya8Vuz/pages/_app.js"
    ></script>
    <script
      src="/_next/static/runtime/webpack-91b117697e716c22a78b.js"
      async=""
    ></script>
    <script
      src="/_next/static/chunks/framework.94bc9fd9a7de53a90996.js"
      async=""
    ></script>
    <script
      src="/_next/static/chunks/commons.6419ced117edf62014da.js"
      async=""
    ></script>
    <script
      src="/_next/static/runtime/main-2ba45c6c0e61dfa5f796.js"
      async=""
    ></script>
    <script
      src="/_next/static/UkokJ2QynwMCza2ya8Vuz/_buildManifest.js"
      async=""
    ></script>
  </body>
</html>

Is there a way to tell next to include this js files inline?

What I tried:

I am trying to use HtmlWebpackPlugin and HtmlWebpackInlineSourcePlugin and updated next.config.js like this:

const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin')

module.exports = {
  exportTrailingSlash: true,
  exportPathMap: async function () {
    const paths = {
      '/': { page: '/' }
    }
    const errorPages = [
      { id: 1, name: 404', lang: 'en' },
      { id: 2, name: '500', lang: 'en' }
    ]

    errorPages.forEach(errorPage => {
      paths[`/errorPage/${errorPage.id}`] = {
        page: '/errorPage/[id]',
        query: { id: errorPage.id }
      }
    })

    return paths
  },
  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    // Note: we provide webpack above so you should not `require` it
    // Perform customizations to webpack config
    // Important: return the modified config
    config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//))
    config.plugins.push(
      new HtmlWebpackPlugin({
        inlineSource: '.(js|css)$' // embed all javascript and css inline
      })
    )
    config.plugins.push(new HtmlWebpackInlineSourcePlugin())
    return config
  }
}

it doesn't transform the resources into the inline format and keeps linking them. Is there a way to achieve the desired result with any other webpack addition or is the config wrong?

thanks in advance!

EDIT: this is my project.json dependency section:

"dependencies": {
    "isomorphic-unfetch": "^3.0.0",
    "next": "latest",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "html-webpack-inline-source-plugin": "0.0.10",
    "html-webpack-plugin": "^3.2.0"
  }


Solution 1:[1]

What I ended up doing is using a StaticDocument instead of the default Document (when in the production environment). Then my StaticDocument is used with CustomHead during export. In there, all link preloads are removed. Like this, no link tag will be rendered in the <head> of the final exported HTML page.

~/pages/_document.js

import Document, { Main, Head } from 'next/document'

class CustomHead extends Head {
  render () {
    const res = super.render()

    function transform (node) {
      // remove all link preloads
      if (
        node &&
        node.type === 'link' &&
        node.props &&
        node.props.rel === 'preload'
      ) {
        return null
      }
      if (node && node.props && node.props.children) {
        return {
          ...node,
          props: {
            ...node.props,
            children: Array.isArray(node.props.children)
              ? node.props.children.map(transform)
              : transform(node.props.children)
          }
        }
      }
      if (Array.isArray(node)) {
        return node.map(transform)
      }

      return node
    }

    return transform(res)
  }
}
   
class StaticDocument extends Document {
  render () {
   return (
      <html>
        <CustomHead />
        <body>
          <Main />
        </body>
      </html>
    )
  }
}

export default process.env.NODE_ENV === 'production'
  ? StaticDocument
  : Document

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 G43beli