I'm trying to integrate Loadable components in my SSR project and that is working when I executed in localhost:3000, then I tried to deploy it in cloud function I am getting this error

[ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined

After that I tried to deploy loadable components server side rendering example, sample code also giving the same error.

I did some changes in package.json, server main.js and app.js file to work in Cloud functions

This is my server main.js file

import path from 'path'
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '@loadable/server'
import App from '../client/App'
const functions = require('firebase-functions');
const app = express()


//app.use(express.static(path.join(__dirname, '../../public')))


const nodeStats = path.resolve(
  __dirname,
  '../../public/dist/async-node/loadable-stats.json',
)

const webStats = path.resolve(
  __dirname,
  '../../public/dist/web/loadable-stats.json',
)

app.get('*', (req, res) => {
  const nodeExtractor = new ChunkExtractor({ statsFile: nodeStats })
  const { default: App } = nodeExtractor.requireEntrypoint()

  const webExtractor = new ChunkExtractor({ statsFile: webStats })
  const jsx = webExtractor.collectChunks(<App />)

  const html = renderToString(jsx)

  res.set('content-type', 'text/html')
  res.send(`
      <!DOCTYPE html>
      <html>
        <head>
        ${webExtractor.getLinkTags()}
        ${webExtractor.getStyleTags()}
        </head>
        <body>
          <div id="main">${html}</div>
          ${webExtractor.getScriptTags()}
        </body>
      </html>
    `)
})

// eslint-disable-next-line no-console
exports.supercharged = functions.https.onRequest(app);

In this file I did these changes from example code app.listen to exports.supercharged = functions.https.onRequest(app); and I imported the const functions = require('firebase-functions');

This is my package.json file

{
  "private": true,
  "scripts": {
    "dev": "nodemon src/server/main.js",
    "build": "NODE_ENV=production yarn build:webpack && yarn build:functions",
    "build:webpack": "webpack",
    "build:functions": "babel -d functions src",
    "start": "NODE_ENV=production node functions/server/main.js",
    "deploy": "firebase deploy --only functions,hosting",
    "serve": "firebase serve --only functions,hosting"
  },
  "devDependencies": {
    "@babel/cli": "^7.4.4",
    "@babel/core": "^7.6.2",
    "@babel/node": "^7.0.0",
    "@babel/preset-env": "^7.6.2",
    "@babel/preset-react": "^7.0.0",
    "@loadable/babel-plugin": "^5.10.3",
    "@loadable/component": "^5.10.3",
    "@loadable/server": "^5.10.3",
    "@loadable/webpack-plugin": "^5.7.1",
    "babel-loader": "^8.0.6",
    "css-loader": "^2.1.1",
    "mini-css-extract-plugin": "^0.6.0",
    "nodemon": "^1.19.0",
    "webpack": "^5.0.0-beta.16",
    "webpack-cli": "^3.3.2",
    "webpack-dev-middleware": "^3.6.2",
    "webpack-node-externals": "^1.7.2"
  },
  "dependencies": {
    "core-js": "^3.0.1",
    "express": "^4.16.4",
    "firebase-admin": "^9.2.0",
    "firebase-functions": "^3.11.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6"
  }
}

In package file I replaced babel script to copy files to functions folder instead of lib

"build:functions": "babel -d functions src", 

Here is my app.js

import React from 'react'
// eslint-disable-next-line import/no-extraneous-dependencies
import loadable from '@loadable/component'

const App = () => (
  <div>
  Hello world
  </div>
)

export default App

Loadable componets has lot of code in app.js so I just replaced app.js with simple hello world text

Here is my firebase.json file

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "function": "supercharged"
      }
    ]
  }
}

These all are the changes I made from the the loadable-components server side rendering async node example

I don't know what I missed here, Please assist me if I missed or need to add anything

3

There are 3 best solutions below

0
On BEST ANSWER

Check by downgrading webpack to 4.31.0, Loadable components has some issues with webpack 5

"webpack": "^4.31.0",
"webpack-cli": "^3.3.2",
"webpack-dev-middleware": "^3.6.2",
"webpack-node-externals": "^1.7.2"
1
On

As @hilipati has pointed out, __dirname is not defined. But there is a more simple solution to that issue, found here

Before using __dirname, you can overwrite the default behaviour like

if(!__dirname) {
    __dirname = path.resolve(path.dirname(''));
}

or, alternatively

if(!__dirname) {
    __dirname = path.resolve();
}
0
On

From my reading of the documentation for path it is unnecessary to provide __dirname to path.resolve(). It will resolve the absolute path for any given relative path including if no parameters are given.

path.resolve(); // => '/root_path/current_working_directory'

With a relative path:

const location = path.resolve('./your_relative_path/file.json')
// OR
const sameLocation = path.resolve('./your_relative_path', 'file.json')
// => '/root_path/current_working_directory/your_relative_path/file.json

Exceptions to this rule are where you enter a non relative/absolute paths as parameters. Then it will attempt to resolve the path right to left until a valid path is formed:

path.resolve('/your_absolute_path/file.json')
// OR
path.resolve('/your_absolute_path', 'file.json')
// => '/your_absolute_path/file.json'

path.resolve('/your_absolute_path', '/file.json')
// => '/file.json'

path.resolve('/one_path', '/another_path', 'file.json')
// => '/another_path/file.json'

So, your resolved code should look as follows:

const nodeStats = path.resolve('../../public/dist/async-node/loadable-stats.json')

const webStats = path.resolve('../../public/dist/web/loadable-stats.json')

This will give you /Volumes/React-Project/SSRAsync/public/dist/async-node/loadable-stats.json and /Volumes/React-Project/SSRAsync/public/dist/web/loadable-stats.json respectively. This still holds true through to Node 16.x