Problem with #NI6 in create your own shell in javascript

I’m stuck on Stage #NI6

I’ve tried to do the challenge of single quotes in echo and cat command. I have done the echo command successfully but can’t complete the cat command.

Here are my logs:

 $ cat '/tmp/foo/f   84' '/tmp/foo/f   67' '/tmp/foo/f   44'
remote: [your-program] node:internal/readline/emitKeypressEvents:74
remote: [tester::#NI6] Output does not match expected value.
remote: [tester::#NI6] Expected: "pineapple blueberry.pineapple strawberry.strawberry grape."
remote: [tester::#NI6] Received: "node:internal/readline/emitKeypressEvents:74"
remote: [your-program]             throw err;
remote: [your-program]             ^
remote: [your-program] <ref *1> Error: spawnSync '/tmp/foo/f ENOENT
remote: [your-program]     at Object.spawnSync (node:internal/child_process:1124:20)
remote: [your-program]     at spawnSync (node:child_process:876:24)
remote: [your-program]     at execFileSync (node:child_process:919:15)
remote: [your-program]     at /app/app/main.js:26:7
remote: [your-program]     at [_onLine] [as _onLine] (node:internal/readline/interface:414:7)
remote: [your-program]     at [_line] [as _line] (node:internal/readline/interface:887:18)
remote: [your-program]     at [_ttyWrite] [as _ttyWrite] (node:internal/readline/interface:1274:24)
remote: [your-program]     at ReadStream.onkeypress (node:internal/readline/interface:264:20)
remote: [your-program]     at ReadStream.emit (node:events:519:28)
remote: [your-program]     at emitKeys (node:internal/readline/utils:371:14) {
remote: [your-program]   errno: -2,
remote: [your-program]   code: 'ENOENT',
remote: [your-program]   syscall: "spawnSync '/tmp/foo/f",
remote: [your-program]   path: "'/tmp/foo/f",
remote: [your-program]   spawnargs: [],
remote: [your-program]   error: [Circular *1],
remote: [your-program]   status: null,
remote: [your-program]   signal: null,
remote: [your-program]   output: null,
remote: [your-program]   pid: 0,
remote: [your-program]   stdout: null,
remote: [your-program]   stderr: null
remote: [your-program] }
remote: [your-program] Node.js v21.7.3
remote: [tester::#NI6] Assertion failed.
remote: [tester::#NI6] Test failed (try setting 'debug: true' in your codecrafters.yml to see more details)


And here’s a snippet of my code:

const { exit } = require("process");
const readline = require("readline");
const pathM=require("path");
const fs=require("fs");
const { execFileSync } = require("child_process");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

// function cat(files) {
//   files.forEach((file) => {
//     fs.readFile(file, 'utf8', (err, data) => {
//       if (err) {
//         console.error(`Error reading file ${file}:`, err);
//         return;
//       }
//       console.log(data);
//     });
//   });
// }

// Uncomment this block to pass the first stage
const types=["echo","exit","type"];
const paths=process.env.PATH.split(":");
function prompt() {
  rl.question("$ ", (answer) => {
    const words=answer.split(" ");
    const firstWord=words[0];
    const words2=answer.split("'");
    
    if(answer==="exit 0"){
      exit(0);
    }else if(firstWord=="echo"){
      const executable=words2.slice(1).join("")? words2.slice(1).join("") : words.slice(1).filter(item => item.trim() !== '').join(" ");
      console.log(executable);
    }else if(firstWord=="cat"){
      let files=words2.slice(1).filter(item => item !== '' && item !== ' '); 
      cat(files);
    }
    else if(firstWord=="type"){
      if(types.includes(words[1])){
        console.log(`${words[1]} is a shell builtin`);
      }else{
        let fullPath = '';
        let found = false;
        for(const dir of paths){
          fullPath = `${dir}/${words[1]}`;
          found = false;
          if(fs.existsSync(fullPath)){
            found = true;
            break;
          }
        }
        if(found){
          console.log(`${words[1]} is ${fullPath}`);
        }
        else{
          console.log(`${words[1]}: not found`);
        }
      }
    }
    else{
      let found = false;
      for (const pathEnv of paths){
        let destPath = pathM.join(pathEnv, firstWord);
        if(fs.existsSync(destPath) && fs.statSync(destPath).isFile()){      
          found = true;  
          execFileSync(firstWord,words.slice(1), { encoding: 'utf-8', stdio: 'inherit' })
        }
      }
      if(!found){
        console.log(`${firstWord}: command not found`);
      }
    }
    
    prompt(); // Recursively call the function to keep the loop going
  });
}
prompt();

Hi, thanks for your post!

I’m currently out of the office and will return on Feb 3. I’ll get back to you as soon as possible after I’m back.

Maybe this page could help you with the readFile() problem, either by using a callback() Function or by using readFileSync() instead.
StackOverflow

Idk NodeJS, but my explanation for the problem is, that the console.log(data) in cat() gets executed while the file is still being read OR the error occurs because the file does not exists and you cannot handle it properly.

const { exit } = require("process");
const readline = require("readline");
const pathM=require("path");
const fs=require("fs");
const { execFileSync } = require("child_process");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

 function cat(files) {
   files.forEach((file) => {
     var data;
     try {
      if (fs.existsSync(filePath)) {
        data = fs.readFileSync(file, 'utf8');
      } else {console.log('File does not exist.'); }
     } catch (err) {
        console.log(`Error reading file ${file}:`, err);
        return;
     }
     console.log(data);
   });
}

Another thing: Before trying to read a file, check if it exists using fs.existsSync.

This second advice will not get your test to pass,
but please not use recursion in the prompt() function.
Use a while(true) - loop instead because it is way more efficient and will not lead to a stack overflow.
For example:

while (true) {
  prompt();
}