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`)
}
Related
Is there any way to stop reading directory after specific point or 3 level tree structure
for example
MainFolder
-ABC Folder
-DEF Folder
-GHI Folder
-image.png
-jkl.gif
when i click on ABC folder my path will look like this /MainFolder/ABC Folder
if there is any other folder or file in it then it will look like
/MainFolder/ABC Folder
-PQR Folder
-STU Folder
-abc.pdf
-xyz.txt
click on PQR folder then it will look like this
/MainFolder/ABC Folder/PQR Folder
-DFC Folder
-HKJ Folder
-mnb.pdf
-xyz.txt
but it should not read DFC Folder/HKJ Folder or any other folder which are present after 3level tree structure
Output :-
["MainFolder/image.png",
"MainFolder/jkl.gif",
"MainFolder/ABC Folder/abc.pdf",
"MainFolder/ABC Folder/xyz.txt",
"MainFolder/ABC Folder/PQR Folder/mnb.pdf",
"MainFolder/ABC Folder/PQR Folder/xyz.txt"]
code which i have it read all files and sub directories but i want to stop at 3level directory
async function getAllFile(folderPath) {
let files = await fs.readdir(folderPath);
files = await Promise.all(
files.map(async (file) => {
const filePath = path.join(folderPath, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
return getAllFile(filePath);
} else if (stats.isFile()) return filePath;
})
);
return files.reduce((all, folderContents) => all.concat(folderContents), []);
}
PS : using node 10.16.3
This should solve your problem. I added the depth parameter, which basically stands for how many folder levels you will want to traverse. In the case of your file tree, you would call this function with depth 2: getAllFile('./MainFolder/', 2) as you want to explore the root (level 1) and the subfolders (level 2), but not the folders within the subfolders (level 3).
I also return null if a folder remains unexplored, as this would otherwise result in undefined values. Before returning I filter out these null values.
async function getAllFile(folderPath, depth) {
depth -= 1;
let files = await fs.readdir(folderPath);
files = await Promise.all(
files.map(async (file) => {
const filePath = path.join(folderPath, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory() && depth > 0) {
return getAllFile(filePath, depth);
} else if (stats.isFile()) return filePath;
else return null;
})
);
return files.reduce((all, folderContents) => all.concat(folderContents), []).filter(e => e != null);
}
I wanted to delete multiple files which are ending with .pdf under the current directory. Suppose I have 3 different pdf files, 1 image file, and one text file, so in these, I want to delete those 3 different pdf files only.
What I have tried.
1st method
fs.unlinkSync('./'+*+pdfname); -> I know this does not make sense
2nd method
try {
var files = (here the list of files should come. However i am failing to get those);
var path="./"
files.forEach(path => fs.existsSync(path) && fs.unlinkSync(path))
} catch (err) {
console.error("not exist")
}
Any different approaches would be appreciated.
Update for the solution:
I have got the solution for my requirement, I just wanted my function to delete all the pdf files and function to be synchronous. However 99% of the solution given by the below author -> https://stackoverflow.com/a/66558251/11781464
fs.readdir is asynchronous and just needs to make it synchronous fs.readdirSync.
below is the updated code and all the credit should go to the author https://stackoverflow.com/a/66558251/11781464.
Updated code
try {
const path = './'
// Read the directory given in `path`
fs.readdirSync(path).forEach((file) => {
// Check if the file is with a PDF extension, remove it
if (file.split('.').pop().toLowerCase() === 'pdf') {
console.log(`Deleting file: ${file}`);
fs.unlinkSync(path + file)
}
});
console.log("Deleted all the pdf files")
return true;
} catch (err) {
console.error("Error in deleting files",err);
}
You can read the directory using fs.readdir and then check for PDF files and delete them. Like this:
fs = require('fs');
try {
path = './'
// Read the directory given in `path`
const files = fs.readdir(path, (err, files) => {
if (err)
throw err;
files.forEach((file) => {
// Check if the file is with a PDF extension, remove it
if (file.split('.').pop().toLowerCase() == 'pdf') {
console.log(`Deleting file: ${file}`);
fs.unlinkSync(path + file)
}
});
});
} catch (err) {
console.error(err);
}
Preliminary Reading
The current working directory of the Node.js process - see update below answer
Path methods
resolve
extName
File System methods
fs.readdirSync
fs.unlinkSync
and classes
dirent
Example
"use strict";
const fs = require('fs');
const path = require('path');
const cwd = process.cwd();
fs.readdirSync( cwd, {withFileTypes: true})
.forEach( dirent => {
if(dirent.isFile()) {
const fileName = dirent.name;
if( path.extname(fileName).toLowerCase() === ".pdf") {
fs.unlinkSync( path.resolve( cwd, fileName));
}
}
});
Notes
untested code
If unlinkSync fails I would assume it returns -1 as per the unlink(2) man page linked in documentation. Personally I would test this using a filename that doesn't exist in cwd.
I believe the {withFileTypes: true} option for readdirSync returns dirent objects with a mime-type value that would allow you to check for files of type application/pdf regardless of extension (not attempted in the example).
Update: path(resolve) adds the current working directory at the beginning of the returned path, when necessary, by default. path.resolve(fileName) will work equally as well as path.resolve(cwd, fileName) in the example.
It seems you know how to delete (unlink) the files - you are asking how to get the file paths?
Try using glob:
const pdfFiles = require("glob").globSync("*.pdf");
Hi here i am attaching tested code for delete all ( only ) .pdf files and not other extension files like .txt , .docs etc from directory.
Note : You can delete any files or directory only from server side.
const fs = require('fs');
const path = require('path')
fs.readdir('../path to directory', (err, files) => {
const pdfFiles = files.filter(el => path.extname(el) === '.pdf')
pdfFiles.forEach(file => {
console.log("Removing File -> ",file);
var filename = "../path to directory/"+file;
fs.unlink(filename,function(err){
if(err) return console.log(err);
console.log('file deleted successfully');
});
});
});
This will gives you a following result in console log.
Removing File -> note.pdf
Removing File -> note2.pdf
file deleted successfully
file deleted successfully
Please feel free to comment any query if have..
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 need help writing a node.js application that searches for all sub directories under the current directory which their names contain the specified string.
for example the user want to search all directories that have the string 'test' in it.
what is the js code i need to use?
i try using this:
var walk = function(dir) {
var results = []
var list = fs.readdirSync(dir)
list.forEach(function(file) {
file = dir + '/' + file
var stat = fs.statSync(file)
if (stat && stat.isDirectory()) results = results.concat(walk(file))
else results.push(file)
})
return results
}
Take a look at node-glob
In your case you could use it like this. This pattern will give you all files in the folder that contain at least once test in the name.
var glob = require("glob")
glob("+(test).js", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is ["**/*.js"]
// er is an error object or null.
if (er) {
// omg something went wrong
throw new Exception(er);
}
var requiredFiles = files.map(function(filename) {
return require(filename);
});
// do something with the required files
});
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))