javascriptnode.jswritefilenode.js-fs

Node file system fs.writeFile() throwing error in JavaScript module


In my index.mjs file I have a function that concatenates and minifies JS utility files. I have it set up like this:

import fs from "fs";
import { minify } from "terser";

function concatenateAndMinifyJsFiles() {
  const jsDir = "./js/";
  const mainJsFile = "./js/main.min.js";

  // Concatenate and minify .js files
  fs.readdir(jsDir, { withFileTypes: true }, (err, files) => {
    if (err) {
      console.error(`Error reading directory ${jsDir}: ${err}`);
      return;
    }

    const jsFiles = files.filter((file) => file.isFile() && file.name.endsWith(".js"));

    if (jsFiles.length === 0) {
      console.log(`No .js files found in ${jsDir}`);
      return;
    }

    const concatenatedJs = jsFiles.map((file) => fs.readFileSync(`${jsDir}${file.name}`, "utf8")).join("\n");
    const minifiedJs = minify(concatenatedJs).code;

    fs.writeFile(mainJsFile, minifiedJs, (err) => {
      if (err) {
        console.error(`Error writing file ${mainJsFile}: ${err}`);
      } else {
        console.log(`File ${mainJsFile} has been created`);
      }
    });
  });
}

I'm getting the following TypeError message:

node:internal/fs/utils:885
  throw new ERR_INVALID_ARG_TYPE(
  ^

TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined

After commenting out each section at a time, it appears that the section that's using the writeFile command:

fs.writeFile(mainJsFile, minifiedJs, (err) => {
  if (err) {
    console.error(`Error writing file ${mainJsFile}: ${err}`);
  } else {
    console.log(`File ${mainJsFile} has been created`);
  }
});

The mainJsFile value is a string. The minifiedJs value from the Terser minify function is also a string (from the concatenatedJs variable).

I'm using Node version 16.15.1. I can't see anything in my code that looks out of place in the Node docs. Any thoughts about why this error is getting thrown?


Solution

  • The error you are seeing from the fs module is correct. The value you are passing for the data argument is not one of the allowed types.

    According to the documentation for Terser at https://www.npmjs.com/package/terser the minify function is asynchronous and therefore you must add await before your call to it. The writeFile call is complaining because the data type is actually undefined, not string. This is because you are trying to access the code property of a Promise, which does not exist (and therefore JavaScript evaluates this as undefined).

    Try using the following:

    const result = await minify(concatenatedJs);
    const minifiedJs = result.code;
    

    Note how I split this into two variables (mainly for clarity) because you cannot access the code property until the result has been awaited.

    Edit: As mentioned by @jfriend00 in the replies to the answer, you will need to also make sure you mark the function containing that await keyword as async. In your case, you'd need to mark your callback function for fs.readdir as async. If you do not do this, you'll get an error as you cannot await other async functions in a non-async function.

    fs.readdir(jsDir, { withFileTypes: true }, async (err, files) => {
        // your code here
    });