Cross platform dynamic file path generation

I have searched extensively but I cant find anything about the best / simplest method of dynamically creating file paths within node-red functions that will work on both a linux and a windows host.

I am modifying an application written by someone else in our company that is intended to be deployed on whatever suitable host is available at the time. The person that wrote it used a windows host, but now I need to deploy it on a Linux host and the paths are all windows style. The biggest issue is "/" vs "" but also linux is case sensitive whereas windows isn't.

Can someone point me to a guide or something on the best practice for making it cross platform ?

Welcome to the forum.

To make it cross platform always use forward slash (which works on both in most cases) and assume that names are case sensitive. Then it will work on both.

As Colin says, Windows supports "/" but prefers "\". If you want a true cross-platform answer, you should be using path.join in your code which will take care of that plus a bunch of other issues for you. That is what I recommend.

Another issues is drive letters and drive paths. Linux doesn't have drive letters and may struggle with drive paths (UNC paths).

Some versions of Windows (and some badly written Windows applications) may also struggle with path lengths and will certainly struggle with folder/file names that are valid on POSIX but not on Windows (such as ones that include : in the name for example).

So there are certainly some things you need to be cautious about but generally if you use path.join and avoid crazy file names and case-sensitive names, you should be fine :grinning:

If your going to be dealing with listing Dir's and all the files in that Dir here is 2 functions I use that deal with path

let fs = require('fs'); //Node.js fs module see https://nodejs.dev/learn/the-nodejs-fs-module
let path = require('path'); //Node.js path module see https://nodejs.dev/learn/the-nodejs-path-module
       async function listDirectories(rootPath)
      {
          const fileNames = await fs.promises.readdir(rootPath);

          if ( fileNames.indexOf(".DS_Store") == 0 ) //for MacOS - if fileNames contains .DS_Store
          {
            fileNames.shift();                       // get rid of it
          }
          const filePaths = fileNames.map(fileName => path.join(rootPath, fileName));
          const filePathsAndIsDirectoryFlagsPromises = filePaths.map(async filePath => ({path: filePath, isDirectory: (await fs.promises.stat(filePath)).isDirectory()}));
          const filePathsAndIsDirectoryFlags = await Promise.all(filePathsAndIsDirectoryFlagsPromises);
          return filePathsAndIsDirectoryFlags.filter(filePathAndIsDirectoryFlag => filePathAndIsDirectoryFlag.isDirectory)
              .map(filePathAndIsDirectoryFlag => filePathAndIsDirectoryFlag.path);
      }

      async function listFiles(rootPath)
      {
          const fileNames = await fs.promises.readdir(rootPath);
          if ( fileNames.indexOf(".DS_Store") == 0 ) //for MacOS - if fileNames contains .DS_Store
          {
            fileNames.shift();                       // get rid of it
          }
          const filePaths = fileNames.map(fileName => path.join(rootPath, fileName));
          const filePathsAndIsFileFlagsPromises = filePaths.map(async filePath => ({path: filePath, isFile: (await fs.promises.stat(filePath)).isFile()}));
          const filePathsAndIsFileFlags = await Promise.all(filePathsAndIsFileFlagsPromises);
          return filePathsAndIsFileFlags.filter(filePathAndIsFileFlag => filePathAndIsFileFlag.isFile)
              .map(filePathAndIsFileFlag => filePathAndIsFileFlag.path);
      }
2 Likes