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
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"
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With