Vercel cannot find static files from node_modules in NextJS - javascript

I have a simple NextJS API page which just fetches a JSON file from one of the node_modules and shows the content at http://localhost:3000/api/my-json. This is the code:
// Path: /pages/api/my-json.ts
import type { NextApiRequest, NextApiResponse } from "next";
import path from "path";
import { promises as fs } from "fs";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
//Find the absolute path of the json directory
const jsonLocation = path.join(
process.cwd(),
"node_modules/#my-company/my-package/lib/my-json-file.json"
);
//Read the json data file data.json
const fileContents = await fs.readFile(jsonLocation, "utf8");
//Return the content of the data file in json format
res.status(200).json(fileContents);
}
It works fine in dev mode, also works when building a production NextJS app by running:
yarn install
yarn build
yarn start
But when I upload the code to Vercel, the app is unable to find the json file, and my page https://my-app.vercel.app/api/my-json returns 500 error:
2022-12-12T10:25:14.142Z 04b6a8fe-53f7-46b6-8c49-98dadf358eb3 ERROR [Error: ENOENT: no such file or directory, open '/var/task/node_modules/#my-company/my-package/lib/my-json-file.json'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/var/task/node_modules/#my-company/my-package/lib/my-json-file.json'
}
Somebody had this problem before? Do I need to copy that JSON file manually somehow during Vercel deployment? How comes it works on local builds but not on Vercel?

Related

NodeJs - Return dynamic filename on response doesn't work

When i response with the static filename everything works fine
return res.download(filePath, 'test.pptx');
however when i response with the dynamic filename, the file comes named as download.
return res.download(filePath, fileName);
The files is coming from folder called ready_files.
The complete function below:
async function downloadPPT(req, res) {
const readyFilesPath = path.resolve(pptProcessFolderPath, 'ready_files');
const files = fs.readdirSync(readyFilesPath);
if(files.length > 1){
const zipFile = files.map(e => {
return {
path: path.resolve(readyFilesPath, e),
name: e
}
});
return res.zip(zipFile, 'PPTs.zip');
}
const filePath = path.resolve(readyFilesPath, files[0])
const fileName = files[0]
return res.download(filePath, fileName);
}
Some details:
When dynamic filename sent, the Content-Disposition header is not coming.
I'am running the code in Docker Container hosted by Google Cloud Run.
When i run docker locally works fine, error occur just on GCP.
When i set static filename on GCP, it's works fine.
ExpressJS version: ^4.17.1
Dockerfile:
FROM node:14
# Create app directory
WORKDIR /home
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm#5+)
COPY package.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
EXPOSE 3000
CMD [ "npm", "start" ]

Vercel Error: ENOENT no such file or directory

I'm building a static blog using Nextjs 13 and deploying it on Vercel. I builded and started the project locally and everything was working, but on Vercel I got this error:
ERROR Error: ENOENT: no such file or directory, open 'posts/second.md' at Object.openSync (node:fs:600:3) at Object.readFileSync (node:fs:468:35) at getPostBySlug (/var/task/.next/server/chunks/207.js:146:63) at Post (/var/task/.next/server/app/posts/[slug]/page.js:603:52) at T (/var/task/.next/server/chunks/760.js:11441:25) at Ma (/var/task/.next/server/chunks/760.js:11604:33) at Array.toJSON (/var/task/.next/server/chunks/760.js:11397:32) at stringify () at V (/var/task/.next/server/chunks/760.js:11696:53) at ping (/var/task/.next/server/chunks/760.js:11496:43) { errno: -2, syscall: 'open', code: 'ENOENT', path: 'posts/second.md'}
The error happens when I go to the "/posts/second" route for example, not in the main page
This is the code interested:
const getPostBySlug = (slug: string) => {
const folder = "posts/";
const file = `${folder}${slug}.md`;
const content = fs.readFileSync(file, "utf8");
return matter(content)
};
The posts folder is located in the root folder.
I tried to modify the next config by adding the output option and setting it to 'standalone':
const nextConfig = {
// config
output: 'standalone',
}
I also tried to modify the path to locate the folder but nothing seems to work.
If more information is needed. project is published on GitHub
I solved my problem by looking at a tutorial provided by Vercel, so thanks to this line of code path.join(process.cwd(), 'posts'); the folder path is resolved.

How should I load package.json for a CLI app installed globally?

I created a cli application which reads its version number from package.json with this bit of code
const packageJson = JSON.parse(fs.readFileSync(path.resolve('./package.json'), 'utf8'))
This works fine if I run the app with yarn start or a similar command while development
But after the package is installed with npm install --global app-name the user should use the declare executable from any path on the system. So if I want to run it say in /Users/myUser/Desktop I get an error like this
Error: ENOENT: no such file or directory, open '/Users/myUser/Desktop/package.json'
So what's a good protocol of loading this package.json within my CLI or is there a better way for approaching this?
Later edit:
For clarity, my package json contains this
{
...
"bin": {
"clip": "./bin/clip.js"
},
...
}
so what I mean by my problem, is when I am running the executable "clip" from a different path, after I used npm publish
After some research I tried this code (use the path.dirname function):
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
export const packageJsonLocation = path.join(__dirname, './../package.json')
const packageJson = JSON.parse(fs.readFileSync(packageJsonLocation, 'utf8'))
and this (just importing the file as json using node's standard import keyword)
import * as packageJson from './../package.json' assert { type: 'json' }
in both cases I get the same result, the executable generated and it tries to read package.json from the current directory. Specifically if I try to console.log() the path I get my current path where I am executing the global executable (clip in my case)
Use __dirname because it always refers to the path of the file that contains this variable, whereas ./ gives you the working directory, such as process.cwd().
const packageJson = JSON.parse(fs.readFileSync(
path.join(__dirname, 'package.json'), 'utf8')
)
If you're using ES Modules, do also to get __dirname
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const packageJson = JSON.parse(fs.readFileSync(
path.join(__dirname, 'package.json'), 'utf8')
)
Edit:
You installed the package globally with a bin, but the bin you're calling with a CLI is a symlink which is inside the path <npm_glob_path>/node_modules/bin not <npm_glob_path>/node_modules/app-name/bin. The package.json of your app is inside <npm_glob_path>/node_modules/app-name. And don't use ./, always use path calls
Hence try this instead (replace app-name by your app's name):
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url))
console.log('__dirname:' + __dirname) // TELL ME WHAT YOU SEE HERE WHEN YOU RUN THE CLI CMD
const packageJsonLocation = path.join(__dirname, '..', 'app-name' 'package.json')
const packageJson = JSON.parse(fs.readFileSync(
path.join(__dirname, 'package.json'), 'utf8')
)
And please, add console.log('__dirname:' + __dirname) after defining __dirname. Which path do you see when you run the CLI app?
is there a better way for approaching this?
Yes - you should store the version number in the actual package itself somewhere. This way it will always be available/accessible and there's no risk of the package.json version and the installed version becoming out of sync. For example, if someone adds your package to a project and then runs yarn install, but later uses git pull to get an up-to-date version of their local files which happens to include a version bump for your package, there is a window where the package.json has a different version number to the installed version.

ENOENT: no such file or directory, open 'Filename'

I was requiring some data from a .json file but I am getting this error:
Error: ENOENT: no such file or directory, open '../Jsons/eshop.json'
at Object.openSync (node:fs:585:3)
at Object.readFileSync (node:fs:453:35)
at Object.execute (C:\Users\Pooyan\Desktop\PDM Bot Main\commands\shop.js:9:24)
at module.exports (C:\Users\Pooyan\Desktop\PDM Bot Main\events\guild\message.js:114:15)
errno: -4058,
syscall: 'open',
code: 'ENOENT',
path: '../Jsons/eshop.json'
}
My code:
let shop_data = JSON.parse(Buffer.from(fs.readFileSync('../Jsons/eshop.json')).toString());
let index = (args[0] || "1");
let page = shop_data.pages[index];
I think that's all you need, but if any other code was needed, comment it.
I am using discord.js v13 and node.js 16
The issue is with the path. It seems the path is invalid for the given eshop.json file or there might be any spelling mistake in the path.
fs.readFileSync takes the relative path:
test.js
JSON
sampleJSON
eshop.json
fs.readFileSync('./JSON/sampleJSON/eshop.json');
You need to use path module and join the folder names to define the json file location.
Let,the data is in following directory. ( NewProject/data/eshop.json )
NewProject
Project root directory
data
eshop.json
You want to access the eshop.json file from any file under the project directory.
Solution is below:
const fs = require('fs')
const path = require('path');
const directory = path.join('data', 'eshop.json')
// directory ==> data\eshop.json
exports.getRandomUserService = () => {
const jsonData = fs.readFileSync(directory);
const eshop= JSON.parse(jsonData);
console.log('eshop data from json file: ',eshop);
}

command not found: nodejs-backend

I'm trying to create my own package that will initialize the boiler plate project by running node package execute npx. I once created one using node.js and inquirer and it worked when I run:
npx ts-config-files.json
This is the package that I've created and published on npm that generates tsconfigs.
The one that I've just created when i run the command:
npx nodejs-backend
I get this:
npx: installed 135 in 47.951s
command not found: nodejs-backend
Instead of getting some inquirer messages. The code of what i'm working on can be found here.
I've been thinking it's because I'm using typescript but when i changed to javascript it's still the same thing.
Sample code of the main file server.ts:
#!/usr/bin/env node
import path from "path";
import inquirer from "inquirer";
import { writeFile, readFile } from "fs/promises";
import fs from "fs";
import helperFunction from "./constants";
import { exec } from "child_process";
import { objJS, objTS } from "./utils";
import chalk from "chalk";
helperFunction.prompt();
const cwd = process.cwd();
const base_name = path.basename(cwd); // node
const main = async () => {
const baseDir: string = "src";
let fileName: string = "";
let packageObject: typeof objTS | typeof objJS;
const { language } = await inquirer.prompt([
{
choices: ["JavaScript", "TypeScript"],
type: "checkbox",
default: "TypeScript",
name: "language",
message: "which language do you want to use for your backend app?",
},
]);
....
You forgot to add:
"bin": {
"nodejs-backend": "server.ts"
},
Which should be in package.json.
Also after that make sure to npm link in your repo before trying to use npx otherwise it will try install it from the https://registry.npmjs.org registry instead of local.
That being said I'm fairly certain it won't execute as typescript even if you've done that (which isn't your issue right now but it will be). For that you can install ts-node and use #! /usr/bin/env ts-node.

Categories