I'm doing a basic operation where I start from a given directory, and I traverse up the filesystem until I hit the root. On Linux/Mac, the root is obviously / and on Windows it can be C:\ or another drive letter of course. My question is whether or not there is a way for Node.js to identify what the root directory of the filesystem is.
Currently, I'm resorting to simply checking the last directory against path.normalize(dir + "/../") to see if it stops changing. Is there a process property/method out there? Maybe a module?
Another one, using path.parse.
const path = require('path')
const getRootDir = () => path.parse(process.cwd()).root
Would this not work?
var path = require("path");
var os = require("os");
var root = (os.platform == "win32") ? process.cwd().split(path.sep)[0] : "/"
There's nothing special that Node.js needs to do, the answer is a simple regex:
/^([^\\/]*[\\/]).*/.test(process.cwd())
var root = RegExp.$1;
That should get the root from the CWD for both Windows and Linux.
I think that the simplest way to do this is simply to check if path.dirname returns the same path as the input path: if it does, then it is the root.
const { dirname } = require('path');
// On Linux, MacOS or other POSIX-like file-systems:
dirname("/") === "/" // -> true
// On Windows:
dirname("C:\\") === "C:\\" // -> true
Here is an example output from Node.js (v14) REPL. Note that path will automatically resolve to path.win32 or path.posix depending on your system, so you don't have to worry about platform compatability:
> path.posix.dirname("/") === "/"
true
> path.win32.dirname("C:\\") === "C:\\"
true
So if you want a utility function for it, you can do something like:
const isRoot = (path) => dirname(path) === path
… or if you want to traverse parents until you hit the root, you can do something like:
function parent(path) {
const parentPath = dirname(path);
return parentPath === path ? null : parentPath;
}
let path = process.cwd();
do {
console.log(path);
} while (path = parent(path))
Related
I'm creating a CLI app and using commander to process the commands entered by the user.
program
.option('-i , --index', 'file ') // option for adding a file/folder
const options = program.opts();
if(option.index){ // do sth }
And the user can enter node index.js --index "Silver Blaze".txt or pass a folder node index.js --index "my folder"
How do I find out the type of the value passed (if it is a file or folder)?
You can use the fs module for that.
Example
let fs = require('fs')
let stats = fs.statSync(/* Path to file/folder */)
let isFile = stats.isFile()
let isDir = stats.isDirectory()
if (isFile) {
// File
} else if (isDir) {
// Directory
}
More information
I open an dialog in electron to select an folder and I want to read out the file path.
But the result.filePaths gives me an filePath with \\ what is not workible for me, to read later the files in the folder. 😅
Result now:
"P:\\Social Media\\Soundboard\\Sounds"
Expected Result:
"P:/Social Media/Soundboard/Sounds"
Is it any way to convert it to "/"? 🤔
My code:
const dialog = require('electron').remote.dialog
dialog.showOpenDialog({
properties: ['openDirectory', 'multiSelections']
}).then(result => {
//Get Selected Folders
folderpath = result.filePaths
console.log(folderpath)
});
Windows uses \ to separate nested resources instead of /. But it supports both. If you still want to convert \\ to /. You can try the below method.
//Get Selected Folders
folderpath = result.filePaths.replaceAll('\\', '/');
console.log(folderpath);
Here's how I develop electron apps to work on both unix and windows without any issues.
Instead of using the path module function, I extended the functionality by using the module below and call that instead. This will sanitize all paths and convert them to correct unix paths like '/var/path/file' or 'C:/path/file'.
Windows can actually work with unix paths for creating/reading/updating/moving files and folders.
export default {
extname (file) {
return path.extname(file)
},
resolve () {
return this.sanitizePath(Array.from(arguments).join('/').replace('//', '/'))
},
normalize () {
const file = this.resolve(...arguments)
return this.sanitizePath(path.normalize(file))
},
basename (file, ext = '') {
return this.sanitizePath(path.basename(file, ext))
},
dirname (file) {
return this.sanitizePath(path.dirname(file))
},
relative (from, to) {
return this.sanitizePath(path.relative(path.resolve(from), path.resolve(to)))
},
sanitizePath (absPath) {
// fix windows separator
return absPath.replaceAll(path.sep, '/')
}
}
The only time I needed windows specific paths was with shell.moveItemToTrash(file) and for that I had to us this client side function
convertPathForWin (file, os = navigator.platform) {
return (os.toLowerCase() === 'win32') ? file.replaceAll('/', '\\') : file
}
Goal: Get a List of Absolute Paths for all files in a directory recursively leveraging NodeJs.
Info: As a python dev, I normally use the python packages which handle this in a platform independent fashion. My boss wanted some javascript code which would handle this goal... and as a JS dev previously, I was like "oh this is easy. Let's look up the node as I never got a chance to get my hands dirty with it." but I seem to be mistaken.
I don't see anything in node relating to Dir Walking, or a way I could hack together to create such a thing.
I was looking in "Child Process", "Console", "File System", "OS", "Path", and "Process". I didn't see anything which would do anything akin to:
pushd .
cd $dir
for folder in $(ls);
do
pushd .
cd $folder
//call again
ls $(pwd)$flag >> $dir/files_matching.txt
popd
done;
// or any platform independent means of recursively getting
// all files and their abs path which meet flag criterion,
// such as "*.txt" || "_*found*"
I could use child process to carry out Command Line items, but then I need to create a bunch of conditionals based on the OS consuming the app, and figured this would be something which already exists.
I don't want to reinvent the wheel, but figured this has already been done; I just don't see it in the base modules.
Is there a node module I would need which accomplishes this, which is outside of the base modules?
I am trying not to have to hand roll a conditional os based system to get an exhaustive list of abs paths for all files under a directory (or subset due to extensions, etc.)
I'd do it like this:
synchronous:
const fs = require("fs");
const { resolve } = require("path");
const getFiles = dir => {
const stack = [resolve(dir)];
const files = [];
while (stack.length) {
dir = stack.pop();
fs.readdirSync(dir).forEach(item => {
const path = resolve(dir, item);
(fs.statSync(path).isDirectory() ? stack : files).push(path);
});
}
return files;
};
console.log(getFiles("."));
asynchronous:
const fs = require("fs");
const { resolve } = require("path");
const pify = require("util").promisify;
const readdir = pify(fs.readdir);
const stat = pify(fs.stat);
const getFiles = async dir => {
const files = await readdir(resolve(dir));
const filesP = files.map(async file => {
const path = resolve(dir, file);
return (await stat(path)).isDirectory() ? getFiles(path) : path;
});
// return (await Promise.all(filesP)).flat(); // flat supported in node ~11
return [].concat(...(await Promise.all(filesP)));
};
getFiles(".").then(console.log);
async demo https://repl.it/#marzelin/getFiles
So, I was looking at the filesystem module and noticed the function readDir
https://nodejs.org/dist/latest-v8.x/docs/api/fs.html#fs_fs_readdir_path_options_callback
which does the trick in part. I guess it wasnt named a method i would have looking for. I was looking for things involving LIST and DIR, but not READ.
Anyways, here is a way to read Dir.
var fs = require('fs');
if (process.argv.length <= 2) {
console.log("Usage: " + __filename + " path/to/directory");
process.exit(-1);
}
var path = process.argv[2];
fs.readdir(path, function(err, items) {
console.log(items);
for (var i=0; i<items.length; i++) {
console.log(items[i]);
}
});
You notice that this one above is Async, but there is a Sync variant, just add "Sync" to the signature. Now you need to determine if something is a directory:
let file = fs.statSync("path/to/directory")
let isDir = file.isDirectory()
So you can couple this all together.
var fs = require('fs')
function recurse_file_system(path, contains) {
let files = fs.readdirSync(path);
let dArr = [];
let fArr = [];
for (let i in files){
let newPath = path + "/" + files[i]
if (fs.statSync(newPath).isDirectory()){
dArr.push(newPath)
}else{
if (filter(files[i], ".txt")){
fArr.push(newPath)
}
}
}
if (arr.length == 0){
return fArr;
}else{
for (let d in dArr){
let rslt = recurse_file_system(dArr[d]);
for (let i in rslt){
fArr.push(rslt[i])
}
}
return fArr;
}
}
console.log("Files:")
console.log(recurse_file_system("/"))
Now if you want to extend this, all you need to do is add a filter to say, limit the size of returns based on particular criterion, such as file name limitation.
function filter(filename, contains){
let reg = new RegEx(contains)
return reg.test(filename)
}
and you can add it to the base case, where you see filter... OR you can just return the WHOLE set and filter it there with the List method, filter.
I am newly with node js, and I would like to find recursively the closest package.json. Actually, continue finding package.json until will not hit it.
My folder tree
root/
-contarats/
-proto/
some.proto
-package.json
"script": {
"contracts": "generate-some-contracts contracts/proto contracts",
}
const input = process.argv[2]
const settings = require(path.resolve(input, 'package.json'))
Are you looking for a way to iterate through directories? If so heres a synchronous function that would do that
function search_sync(dir) {
var results = []
var list = fs.readdirSync(dir)
list.forEach(function(file) {
file = path.resolve(dir, file)
filename = file.split('\\');
filename = filename[filename.length-1]
var stat = fs.statSync(file)
if (stat && stat.isDirectory()) results = results.concat(search_sync(file))
else if(filename == 'package.json')results.push(file)
})
return results
}
That will return an array of any files that are named package.json with their full file path. EG:
search_sync('./')
[C:\Users\User\MyNodeProject\package.json,
C:\Users\User\MyNodeProject\npm\someDependency\package.json,
C:\Users\User\MyNodeProject\npm\someOtherDependency\package.json]
Personally, I'd then break each line by their '\' character and see which one is closer to my root folder
Looking at your tree of directories, the package.json file is not in contracts/proto, but in contracts. (I assume that contaracts is a typo.) Changing the first argument on the command line should help:
generate-some-contracts contracts contracts
Nevertheless, you ask about the recursive search for the nearest package.json. NPM does it, when looking for the package root. It starts in the current directory and then follows the ancestors, until it finds a package.json. A function reading and parsing that package.json, similarly to require, could look like this:
const { readFile } = require('fs/promises')
const { join, resolve } = require('path')
async function loadPackageJson(cwd) {
const startDir = cwd || process.env.INIT_CWD || process.cwd()
let dir = startDir, prevDir
do {
try {
const path = join(dir, 'package.json')
const content = await readFile(path, 'utf8')
return JSON.parse(content)
} catch (err) {
if (err.code !== 'ENOENT') throw err
}
prevDir = dir
dir = resolve(dir, '..')
} while (prevDir !== dir)
throw new Error(`package.json not found in ${startDir} and its ancestors`)
}
I am using the below code to split up a user provided path, create all intermediate dirs in the path and attach a timestamp to the ending file name. I am splitting the path with / first and then using forEach over the resulting array. Is there any better way/direct lib function to do this in javascript / nodejs.
function test(usrPath) {
var locMatches = usrPath.split("/")
locMatches.forEach (function (item) {
location = pathUtils.join(location,item)
if (!fs.existsSync(location)) {
fs.mkdirSync(location)
}
})
return pathUtils.join (location,usrPath + (new Date).toISOString().replace(/(^\d\d\d\d)|-|:|(\..*)/g,"").replace("T","_")+".log")
}
Ok, so there are path utils that allow to make the implementation better across platform.
Also, it gives a better managed access for working with path elements like root, dir, filename and extension. pathUtils.sep allows working on the dir elements more cross platform.
var pathUtils = require('path')
function test(usrPath) {
var pathElements = pathUtils.parse(usrPath)
pathElements.dir.split(pathUtils.sep).forEach (function (item) {
location = pathUtils.join(location,item)
if (!fs.existsSync(location)) {
fs.mkdirSync(location)
}
})
return pathUtils.join (location,pathElements.name + (new Date).toISOString().replace(/(^\d\d\d\d)|-|:|(\..*)/g,"").replace("T","_")+pathElements.ext.replace(/^$/i,".log"))
}