fs.readFile() or fs.readFileSync() not a function exception but why? - javascript

I am using the fs module with the following import code
import fs = require('fs')
The code runs until encountering this exception at the second line of the TypeScript codes below
const filePath = 'data/soylent-uist2010/userSegments.json'
const seg = fs.readFileSync(filePath, {
encoding: 'utf8',
})
However, if I supply the path argument of readFileSync as a raw string (as below), it works as normal (value is assigned).
const seg = fs.readFileSync('data/soylent-uist2010/userSegments.json', {
encoding: 'utf8',
})
The error stack trace is as below,
Viewer.tsx:155 Uncaught (in promise) TypeError: fs.readFileSync is not a function
at Viewer.<anonymous> (Viewer.tsx:155)
at step (io.ts:106)
at Object.next (io.ts:106)
at io.ts:106
at new Promise (<anonymous>)
at __awaiter (io.ts:106)
at Viewer._this.loadFiles (Viewer.tsx:135)
at Viewer.<anonymous> (Viewer.tsx:98)
at step (io.ts:106)
at Object.next (io.ts:106)
A longer code snippet is as below. I suspect if the async keyword (in the class method) would require an await keyword before fs.readFile()
loadFiles = async () => {
this.setState({ pages: [] });
const {
pageNumbersToLoad,
pathInfo: { pdfDir, pdfRootDir }
} = this.props;
const fullDirPath = path.join(pdfRootDir, pdfDir);
const pdfPath = path.join(fullDirPath, pdfDir + ".pdf");
**const seg = fs.readFile(...);**

Because fs does not have a default export you need to import like so:
import * as fs from 'fs'

You are mixing javascript standards.
If you choose to use the older javascript ES5 standard, then your code should like like this:
var fs = require('fs');
However, if you're going to use the newer ES6 (and beyond) standard, you would use the import statement like so:
import fs from 'fs';
You seem to be combining the two, hence your error.
NOTE: Because fs has a default export that exports the fs module, you don't need the import * as syntax. See this post for a more detailed explanation.

Related

I am getting a required error using graphql tools

I am building a gql server application using apollo server.
When I try to load my .graphql files using import { loadFilesSync } from '#graphql-tools/load-files', this works very well, but when I load my resolver files, i get an error
node:internal/errors:464
ErrorCaptureStackTrace(err);
^
Error [ERR_REQUIRE_ESM]: require() of ES Module /userpath/index.js from /userpath/server-gql/noop.js not supported.
Instead change the require of index.js in /userpath/server-gql/noop.js to a dynamic import() which is available in all CommonJS modules.
at Object.newLoader [as .js] (/userpath/server-gql/node_modules/pirates/lib/index.js:141:7)
at file:///userpath/server-gql/node_modules/#graphql-tools/load-files/esm/index.js:104:33
at Array.map (<anonymous>)
at loadFilesSync (file:///userpath/server-gql/node_modules/#graphql-tools/load-files/esm/index.js:95:10)
at file:///userpath/server-gql/schema.js:20:24
at async Promise.all (index 0) {
code: 'ERR_REQUIRE_ESM'
}
I am using "type": "module" in my package.json.
Here's my code snippet for where i get the error
import path from 'path'
import { fileURLToPath } from 'url'
import { loadFilesSync } from '#graphql-tools/load-files'
import { mergeTypeDefs, mergeResolvers } from '#graphql-tools/merge'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const typesArray = loadFilesSync(path.join(__dirname, '.'), {
recursive: true,
extensions: ['graphql'],
})
const resolversArray = loadFilesSync(path.join(__dirname, './graphql/**/*.resolvers.js'), {
recursive: true,
extensions: ['js'],
})
const newResolversArray = resolversArray.slice(1)
export const typeDefs = mergeTypeDefs(typesArray)
export const resolvers = mergeResolvers(newResolversArray)
I think the error occurs in the resolvers array.
I was running into a very similar issue and found this workaround/solution worked for me: https://github.com/ardatan/graphql-tools/issues/1750#issuecomment-655828240
I had to modify that answer a bit to get it working in my codebase...below is what my own resolverFiles snippet now looks like (I'm not 100% sure what your import/naming conventions look like, or I'd try to apply this to your code snippet).
I have a mixed, nested directory schema/ that contains resolver files of the format SomethingResolvers.ts. Each of these look like the following (I'm using codegen):
import { Resolvers } from "../../generated/graphql";
export const resolvers: Resolvers = { /* resolver implementations */ };
const resolverFiles = await loadFiles(
path.join(__dirname, "**/*Resolvers.js"),
{
requireMethod: async (path) => {
const module = await import(pathToFileURL(path).toString());
return module["resolvers"];
},
recursive: true,
}
);

Why are imported modules on serverless functions undefined?

TL;DR - why do imports return undefined? Especially for native Node modules like path?
I have a very small test application, built with Vite, that has a single endpoint in the /api directory. I created this just to play around with Vite + Vercel.
I have only 2 module imports for my endpoint - path and fs-extra. Both are returning as undefined. I was getting cannot read join of undefined errors with the path module, so I wrapped everything in a try/catch just to see if the endpoint responds. It does. See my code below.
import type {VercelRequest, VercelResponse} from '#vercel/node'
import path from 'node:path' // I've also tried 'path'
import fs from 'fs-extra'
export default function handler(req: VercelRequest, res: VercelResponse) {
// Both of these log 'undefined' on my Vercel dashboard for function logs.
console.log('path module:', path)
console.log('fs module:', fs)
try {
// https://vercel.com/guides/how-can-i-use-files-in-serverless-functions
const pathFromProjectRootToFile = '/api/usersData.json'
const usersDataFilePath = path.join( // THIS IS WHERE THE ERROR HAPPENS 🙃
process.cwd(),
pathFromProjectRootToFile
)
const usersData = fs.readJSONSync(usersDataFilePath)
res.json({users: usersData})
} catch (e) {
res.json({error: errorToObject(e as Error)})
}
}
function errorToObject(err?: Error): Record<string, string> | null {
if (!err || !(err instanceof Error)) return null
const obj: Record<string, string> = {}
Object.getOwnPropertyNames(err).forEach(prop => {
const value = err[prop as keyof Error]
if (typeof value !== 'string') return
obj[prop] = value
})
return obj
}
As an aside, instead of node:path I also tried just path, but same thing - undefined. And I do have fs-extra as a dependency in my package.json.
When deploying a Vite app on Vercel and utilizing the /api directory for a back end with Serverless Functions, Node modules need to be imported via require, not import. The import syntax works for types and local modules. See this answer (and conversation) in Vercel's community discussions.
import type {VercelRequest, VercelResponse} from '#vercel/node'
import localModule from '../myLocalModule'
const path = require('path')
const fsOriginal = require('fs')
const fs = require('fs-extra')
// Rest of the file here...

SyntaxError: Unexpected reserved word "await"

I keep getting this error even thought I am in a asynchronous function...
import { dirname, join } from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export async function importer(client) {
const dir = join(__dirname, "../../commands/")
const commandCategories = readdirSync(dir)
for (let cat of commandCategories) {
const commandFiles = readdirSync(join(dir, cat)).filter(files => files.endsWith(".js"));
for (const file of commandFiles) {
const command = await import(join(dir, cat, file));
client.commands.set(command.default.name, command.default);
}
}
}
Is there something I am missing? I definitely need the await part in import in const command. It imports a few commands then it drops the mentioned error in title on the console output.
Thanks.
The problem isn't the importer code, it's in one of the modules you attempted to import (and import crashes on parsing it)!
It's a bug in node that such a non-helpful error is thrown when this syntax error is encountered during dynamic import.
Try logging the filenames before importing them so you can see at which file it crashes. Once you know which file failed to parse, do a static import of it for testing, so you can get a proper error that tells you which line has the issue.

import async function from the module

I have a function that looks like this
let fileCache = {};
async function generateFromFile(fullPath) {
if (!fileCache[fullPath]) {
let jsonResource = await fs.readFile(fullPath);
fileCache[fullPath] = JSON.parse(jsonResource);
}
return fileCache[fullPath];
}
When I use it in the same module like this
await generateFromFile('myfile.json');
it works fine.
I created a separate file, put the function there and exported it like this
let fileCache = {};
async function generateFromFile(fullPath) {
if (!fileCache[fullPath]) {
let jsonResource = await fs.readFile(fullPath);
fileCache[fullPath] = JSON.parse(jsonResource);
}
return fileCache[fullPath];
}
module.exports = {
generateFromFile: generateFromFile
}
I imported it like this
const utils = require('../util/utils.js');
and wanted to use like await utils.generateFromFile('myfile.json'); but it fails with the error
TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined
What is the problem?
How did you import your fs module. To use readFile with await your should
either write fs.promise.readFile
or import fs as
import { promises as fs } from 'fs'
Are you sure that your newly created file is in the relative directory ../util/utils.js? Your export looks fine. Maybe try to print out the imported utils object to see what you actually imported.
EDIT: You are also passing relative filenames to your function which is now in a different diretory than before. When accessing fs.readFile(fullPath) it tries to read the file inside the directory "util/". You should call your function with a full filepath like util.generateFromFile(path.join(__dirname, "myfile.json")) using the path node package.
EDIT 2: Like Raeesaa state you are using fs.readFile wrong. It does not return a Promise but expects a callback. To is it with await you have to promisify it like so:
const fs = require("fs");
const { promisify } = require("util");
const readFileAsync = promisify(fs.readFile);
let fileCache = {};
async function generateFromFile(fullPath) {
if (!fileCache[fullPath]) {
let jsonResource = await readFileAsync(fullPath);
fileCache[fullPath] = JSON.parse(jsonResource);
}
return fileCache[fullPath];
}
module.exports = {
generateFromFile: generateFromFile
}
EDIT 3: If you only import JSON files you can do it by using require. Internally, require has also a cache so imports are only executed once. You can import JSON files as objects like so: const obj = require("myfile.json");

Issue with mkdirp from fs-promise and co

I have a generator function that creates a directory and performs operations on it once done:
import co from 'co'
import map from 'lodash/fp/map'
import path from 'path'
import {mkdirp} from 'fs-promise'
import co from 'co'
export const createDirectoryForMake = (parentDir,makeName) => {
const dirPath = path.join(parentDir,makeName)
return mkdirp(dirPath)
}
function getGenerateMakeOutput(parentDir,byMake,makeName){
function * generateMakeOutput() {
try {
console.log('before create directory for make')
const dirStatus = yield createDirectoryForMake(parentDir,makeName)
} catch(e) {
console.error(e)
}
}
co(generateMakeOutput)
}
This is the output that I get when I execute the code:
TypeError: You may only yield a function, promise, generator, array, or object, but the following object was passed: "undefined"
at next (/home/vamsi/Do/redbubble-demo/node_modules/co/index.js:101:25)
at onFulfilled (/home/vamsi/Do/redbubble-demo/node_modules/co/index.js:69:7)
at run (/home/vamsi/Do/redbubble-demo/node_modules/core-js/modules/es6.promise.js:89:22)
at /home/vamsi/Do/redbubble-demo/node_modules/core-js/modules/es6.promise.js:102:28
at flush (/home/vamsi/Do/redbubble-demo/node_modules/core-js/modules/_microtask.js:18:9)
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
UPDATE:
I found the issue, it is not with the code in the sample there but with a line below it where I wrote this gem:
yield Promise.all[promiseA,promiseB]

Categories

Resources