Vercel: ERROR Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild - reactjs

Hope you all are having a good time. I am working on a simple NextJs application where I want to have multiple subdomains in it. I am deploying the application on vercel.
What my application does is it have a simple textarea where you write MDX, you click the Publish button and it will save that MDX into firebase firestore. Below the textarea it shows the list of all the pages that have been published before.
The application renders the list of all the pages like with name of the page which is randomly generated as the subdomain while the actual domain comes later like the following.
a-mdx-page.mydomain.app
When I open that URL it will fetch the page MDX from firestore and uses next-mdx-remote package to serialize and render the MDX. The reason for using the next-mdx-remote package is that we can add react components in MDX and it can render as normal react components. I already have a custom domain because you cannot have a subdomain on top of a free subdomain in vercel free deployment.
All works fine on localhost and everything is as it should be but the problem is when I deploy the code on Vercel and navigate to subdomain it shows ERROR 500 on the website and shows the following error in the logs.
[GET] / 21:21:03:30
2021-10-24T16:21:04.018Z 8e52d5da-ff1f-4840-a09b-199233834a5d ERROR Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild.
If you are installing esbuild with npm, make sure that you don't specify the
"--no-optional" flag. The "optionalDependencies" package.json feature is used
by esbuild to install the correct binary executable for your current platform.
at generateBinPath (/var/task/node_modules/esbuild/lib/main.js:1643:15)
at esbuildCommandAndArgs (/var/task/node_modules/esbuild/lib/main.js:1699:11)
at ensureServiceIsRunning (/var/task/node_modules/esbuild/lib/main.js:1856:25)
at Object.transform (/var/task/node_modules/esbuild/lib/main.js:1751:37)
at serialize (/var/task/node_modules/next-mdx-remote/dist/serialize.js:287:43)
at async getServerSideProps (/var/task/.next/server/pages/index.js:261:25)
at async Object.renderToHTML (/var/task/node_modules/next/dist/server/render.js:428:24)
at async doRender (/var/task/node_modules/next/dist/server/next-server.js:1144:38)
at async /var/task/node_modules/next/dist/server/next-server.js:1236:28
at async /var/task/node_modules/next/dist/server/response-cache.js:64:36 {
page: '/'
}
RequestId: 8e52d5da-ff1f-4840-a09b-199233834a5d Error: Runtime exited with error: exit status 1
Runtime.ExitError
From what I understand that the next-mdx-remote serialize function uses esbuild in it and when I deploy the application on vercel npm just doesn't downloads the platform specific package of it but may be I am wrong.
I have tried to search the solution for this but there is not any answers that helped me.
Following is all the code that the application uses.
import { useState } from "react"
import { collection, doc, getDoc, getDocs, setDoc } from "firebase/firestore"
import matter from "gray-matter"
import { MDXRemote } from "next-mdx-remote"
import { serialize } from "next-mdx-remote/serialize"
import {
uniqueNamesGenerator,
adjectives,
colors,
animals,
} from "unique-names-generator"
import { db } from "../utils/fire-client"
import Layout from "../components/Layout"
import { HOSTNAME } from "../config"
import MDXComponents from "../components/mdx"
export default function Index({ posts, isPage = false, mdxSource }) {
const [mdxCode, setMdxCode] = useState("# THIS IS MDX")
const [message, setMessage] = useState("")
const addPageToCollection = async (name, content) => {
const pagesCollection = collection(db, "pages")
await setDoc(doc(pagesCollection, name), {
name,
content,
})
}
function publishPage() {
const randomName = uniqueNamesGenerator({
dictionaries: [adjectives, colors, animals],
})
addPageToCollection(randomName, mdxCode)
setMessage(
"New Page Added: " + randomName + "\nReload page To see it in the list"
)
setTimeout(() => {
setMessage("")
}, 5000)
}
return (
<Layout>
{isPage ? (
<>
<header>
<nav>
<a href={"http://" + HOSTNAME}>
<a>👈 Go back home</a>
</a>
</nav>
</header>
<main>
<MDXRemote {...mdxSource} components={MDXComponents} />
</main>
</>
) : (
<>
<h1>Home Page</h1>
<textarea
name="mdxCode"
id="mdxCode"
value={mdxCode}
onChange={(e) => setMdxCode(e.target.value)}
className="w-full h-1/2 border-2 border-gray-400 p-2"
/>
<button className="btn btn-primary" onClick={publishPage}>
Publish
</button>
<div>{message}</div>
<ul>
<div className="mt-4 font-bold">Pages List</div>
{posts.map((post) => (
<li key={post.name}>
<a href={`http://${post.name}.${HOSTNAME}`}>{post.name}</a>
</li>
))}
</ul>
</>
)}
</Layout>
)
}
export async function getServerSideProps({ req, res }) {
const host = req.headers.host.split(".")
if (host[0] !== HOSTNAME.split(".")[0] && host[0] !== "www") {
const docRef = doc(db, "pages", host[0])
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
const { content, data } = matter(docSnap.data().content)
const mdxSource = await serialize(content, {
// Optionally pass remark/rehype plugins
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [],
},
scope: data,
})
if (mdxSource) {
return {
props: {
isPage: true,
mdxSource,
},
}
}
} else {
return {
props: {
redirect: {
destination: "/",
},
},
}
}
}
const pagesCollection = collection(db, "pages")
const pagesSnapshot = await getDocs(pagesCollection)
const pagesList = pagesSnapshot.docs.map((doc) => doc.data())
if (pagesList.length > 0) {
return {
props: {
posts: pagesList,
},
}
}
return { props: { posts } }
}

Update esbuild to 0.13.4 or higher
npm i -D esbuild#0.13.4
See: https://github.com/evanw/esbuild/releases/tag/v0.13.4

If building with Docker this could be due to an incomplete .dockerignore file that doesn't ignore all your node_modules folder.

Related

JSX fragment content is not rendering

[postslug].js
import {PostData} from '../../data/postdata'
export async function getStaticProps({ params }) {
const posts = PostData.find((p) => p.slug === params.postslug);
return {
props: {
post: posts,
},
};
}
export async function getStaticPaths() {
const paths = PostData.map((post) => ({
params: { postslug: post.slug },
}));
return { paths, fallback: false };
}
const Post = ({post}) => {
// const router = useRouter();
// const slug = router.query.postslug;
// const post = PostData.find((post1) => post1.slug === slug);
return (
<>
{post.content}
</>
)
}
PostData.js
export const PostData = [
{
id: 1,
slug: "article-blog",
content: `<><main>
<div className="postpage">
<section className="article">
<article>
<h2>Google Chrome</h2>
<p>Google Chrome is a web browser developed by Google, released in 2008. Chrome is the world's most popular web browser today!</p>
</article>
<article>
<h2>Mozilla Firefox</h2>
<p>Mozilla Firefox is an open-source web browser developed by Mozilla. Firefox has been the second most popular web browser since January, 2018.</p>
</article>
<article>
<h2>Microsoft Edge</h2>
<p>Microsoft Edge is a web browser developed by Microsoft, released in 2015. Microsoft Edge replaced Internet Explorer.</p>
</article>
</section>
</div>
</main></>`
},
]
The code written in JSX Fragments is not rendering after fetching it. It is just displayed as it is written in an array of objects. Postdata.js file is containing an array of objects. I am trying to fetch the data of blog articles using getStaticProps and getStaticPaths.
Output Like:
The first solution can be using dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{__html: post.content}} />
But as for the security problem, it will lead you to cross-site scripting (XSS) attack. So I'd propose you use html-react-parser that will help you to render a string as JSX safely.
import parse from 'html-react-parser';
const Post = ({post}) => {
return (
<>
{parse(post.content)}
</>
)
}
You have to use dangerouslySetInnerHTML in React, it is equivalent to seting innerHTML in vanilla JS refer this React.js: Set innerHTML vs dangerouslySetInnerHTML

Building query with NextJS, Prisma, and ClerkJS

I'm prototyping a project using NextJS, Prisma, and ClerkJS. I'm trying to understand how I would supply various params/props to my Prisma search clause. In particular I need to get the email address of a user from ClerkJS. This is my current index file:
import React from "react";
import prisma from "../../prisma/initPrisma"
const FacilitiesPage = ({ facilities }) => {
return (
<div className={styles.dashCards}>
{facilities.map((facility) => {
return (
<div className={styles.card} key={facility.id}>
<h4>{facility.name}</h4>
</div>
);
})}
</div>
);
};
export async function getStaticProps() {
const facilities = await prisma.facility.findMany({
where: {
ownerEmail: 'harcodedemail'
},
});
return {
props: {
facilities,
},
};
}
export default FacilitiesPage;
Obviously I can't hardcode the email address of every user in the system. ClerkJS offers several ways to query the user object and return various things from it, which I could pass into getStaticProps (or getServerSideProps probably). But nothing I've tried works. Candidly, I'm still learning the "React way" to do a lot of things.
TL;DR: how do I supply props to the query string in getStaticProps?
The folks at Clerk.dev just answered this question. You need to use getServerSideProps and then use the new "withServerSideAuth" component. Here is a snippet from the blog post https://clerk.dev/blog/next-js-ssr-authentication-with-clerk :
import { withServerSideAuth } from "#clerk/nextjs/ssr";
export const getServerSideProps = withServerSideAuth(async ({ req, resolvedUrl }) => {
const {sessionId,getToken} = req.auth;
if (!sessionId) {
return { redirect: { destination: "/sign-in?redirect_url=" + resolvedUrl } };
}
// use a token for your Clerk integrations
const hasuraToken = await getToken({ template: 'hasura' });
// retrieve data from your Hasura integration
return { props: {} };
});

Showing 404 error after hosting a webapp in react js

I have created a Blog web App using react Js , it is working fine on my local system but after hosting it ,it is showing 404 status code . I have used fake json-server package to launch on my local system but after hosting it with the github it is fetching no data from json file.
This is the webapp https://manishya1669.github.io/BlogWebApp/ and this is the code for Blogdetails and for more clearity you can see the whole code source on https://github.com/manishya1669/BlogWebApp
import { useHistory, useParams } from "react-router-dom";
import useFetch from "./useFetch";
const BlogDetails = () => {
const {id} = useParams();
const {data:blog, error,isPending} = useFetch('http://localhost:8000/blogs/' + id);
const history = useHistory();
const handleClick = () => {
fetch('http://localhost:8000/blogs/' + blog.id, {
method: 'DELETE'
}).then(() => {
history.push('/');
})
}
return (
<div className= "blog-details">
{isPending && <div> Loading</div> }
{error && <div>{error}</div>}
{ blog &&(
<article>
<h2>
{blog.title}
</h2>
<p> Written by {blog.author}</p>
<div>{blog.body}</div>
<button onClick={handleClick}>Delete</button>
</article>
)
}
</div> );
}
export default BlogDetails;
**Hope this is enough to make my self clear and appreciating those who are willing to help**
I found its solution , it was not following Cors policy because
I was running my whole Client side on port no 3000 and trying to receive data from port 8000, So due to two different port it was considering two different website where one is trying to retrieve data from other also that website was not secure . So to deal with this you have add a proxy server in your package.json for example
"name": "webapp",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:8000",
Also you have to replace fetch('http://localhost:8000/blogs/' + blog.id,
with fetch('/blogs/' + blog.id,
const handleClick = () => {
fetch(/'blogs'/+ blog.id, {
method: 'DELETE'
}).then(() => {
history.push('/');
})
}
If you find and any mistake please let me know it would help me to learn more.

movie-trailer npm isn't working properly in my netflix clone project

I've made a netflix clone using React.js, firebase, and have used TMDB api for movie database.
I've also used react-youtube and movie-trailer npm.
So it has this feature that every time I click on any movie poster, it's trailer must be played.
But for most of the movies, trailer doesn't shows up.
This is the error that I face -
movie-trailer: No TMDB Movie found with the current search terms, try searching https://www.themoviedb.org/search?query=Luis%20Miguel%3A%20The%20Series
TypeError: Failed to construct 'URL': Invalid URL
at Row.js:37
'This is the screenshot of errors I am facing on clicking maximum of movies'
I'm sharing the link of my github repo and deployed website as well for reference -
github - https://github.com/IshitaSharma3101/netflix-clone
website - https://netflix-clone-afb8b.web.app/
Row component code-
import React, { useState, useEffect } from "react";
import YouTube from "react-youtube";
import axios from "./axios";
import "./Row.css";
import movieTrailer from "movie-trailer"
const base_url = "https://image.tmdb.org/t/p/original/";
function Row({ title, fetchURL, isLargeRow }) {
const [movies, setMovies] = useState([]);
const [trailerURL, setTrailerURL] = useState("");
useEffect(() => {
async function fetchData() {
const request = await axios.get(fetchURL);
console.log(request.data.results);
setMovies(request.data.results);
return request;
}
fetchData();
}, [fetchURL]);
const opts = {
height: "390",
width: "100%",
playerVars: {
autoplay: 1,
},
};
const handleClick = (movie) => {
if (trailerURL) {
setTrailerURL("");
} else {
movieTrailer(movie?.name || movie?.title || movie?.original_title || "")
.then((url) => {
const urlParams = new URLSearchParams(new URL(url).search);
setTrailerURL(urlParams.get("v"));
})
.catch((error) => console.log(error));
}
};
return (
<div className='row'>
<h2>{title}</h2>
<div className='row__posters'>
{movies.map((movie) => (
<img
key={movie.id}
onClick={() => handleClick(movie)}
className={`row__poster ${isLargeRow && "row__posterLarge"}`}
src={`${base_url}${
isLargeRow ? movie.poster_path : movie.backdrop_path
}`}
alt={movie.name}
/>
))}
</div>
{trailerURL && <YouTube videoId={trailerURL} opts={opts} />}
</div>
);
}
export default Row;
I'm sure you are following the Clever Qazi tutorial as I'm doing. I think there is basically no solution for this problem. I also try to get the trailer of the movies by the ID that TMDB gives for every film but this method definitely doesn't work for mine. My conclusion is that movie-trailer is not working for certain kind of film or series...
You can prove yourself my conlclusion by typing on a terminal npx movie-trailer Cobra Kai
which is the command that allow you to use this package. Cobra Kai is a well know series but this "tool" doesn't find any trailer for it.
Use this instead.
movieTrailer(null ,{ tmdbId: movie.id })
.then((url)=>{
console.log("url is "+url);
const urlParams=new URLSearchParams(new URL(url).search);
console.log("urlParamsn"+urlParams);
setTrailerUrl(urlParams.get("v"));
})
.catch((error)=> console.log(error));
}
}
With this you can search for the movie with the tmdb id.
This error is coming becouse TMDB has not added ids for some videos if you want to play all video you just need need to change the genres of the Netflix Original Video from the path in request.js folder where you wrote your path becouse Netflix originals Videos has not linked with ids on TMDB.

AWS Amplify - How to render components after sign in

I have an AWS Amplify app using React. I want to be able to only load (or reload) a TaskList component only when the user has successfully signed in. However, the component gets rendered from the very beginning when page loads and when user fills up form and gets signed up it won't reload. I have been trying multiple workarounds but I can't see how to make my component depend on a successful login. I rely on the default Amplify authenticator functions to sign the user in against Cognito.
const App = () => (
<AmplifyAuthenticator>
<div>
My App
<AmplifySignOut />
<TaskList />
</div>
</AmplifyAuthenticator>
);
I managed to solve it using hints given in this answer AWS Amplify: onStatusChange then render main page.
Basically, I changed my App component to return only sign in form or the whole up based on auth state change.
const App = () => {
const [authState, setAuthState] = useState('');
function handleAuthStateChange(state) {
if (state === 'signedin' || state === 'signedout') {
setAuthState(state);
}
}
return (
<div>
{ authState !== 'signedin' ?
<AmplifyAuthenticator>
<AmplifySignIn handleAuthStateChange={handleAuthStateChange} slot="sign-in"></AmplifySignIn>
</AmplifyAuthenticator>
:
<div>
My App
<AmplifySignOut handleAuthStateChange={handleAuthStateChange} slot="sign-out"/>
<TaskList />
</div>
}
</div>
);
}
This is how I solved a similar issue to manage the states. I was having some problems as it didn't seem to dispatch the events afterwards.
From https://github.com/aws-amplify/amplify-js/issues/5825
import React from 'react';
import { AmplifyAuthenticator, AmplifySignOut, AmplifySignUp, AmplifySignIn} from '#aws-amplify/ui-react';
import { onAuthUIStateChange } from '#aws-amplify/ui-components'
const Panel = () => {
const [setAuthState] = React.useState();
React.useEffect(() => {
return onAuthUIStateChange(newAuthState => {
if(newAuthState === 'signedin'){
// Do your stuff
}
setAuthState(newAuthState)
});
}, []);
return(
<AmplifyAuthenticator>
<AmplifySignIn headerText="Sign In" slot="sign-in"/>
<AmplifySignUp slot="sign-up" formFields={[
{type: 'username'},
{type: 'email'},
{type: 'password'}
]}></AmplifySignUp>
<AmplifySignOut></AmplifySignOut>
</AmplifyAuthenticator>
)
}
export default Panel;

Resources