Is there any clean way to check if a path is a file or directory?
Currently I am using this:
exports.isDirectory = (dirPath) => {
return fs.lstatSync(dirPath).isDirectory()
}
But my problem here is that if dirPath does not exist yet, then lstatSync gives out an error.
Then I also tried:
exports.getFileName = (filePath) => {
return filePath.split(/[\\\/]/).pop()
}
exports.isDirectory = (dirPath) => {
return exports.getFileName(dirPath) === ''
}
And call:
const home = require('os').homedir()
const sampleLoc = path.join(home, '/.folder/another'))
isDirectory(sampleLoc)
But it only basically thinks another is the filename and returns false on isDirectory.
Can't really check for the presence of . (like an extension of a file name) since my folders can have dots anywhere on its name.
How can I check if the given path is for a file or directory? (considering it does not exist yet at the point of checking)?
Help!
Related
How can I loop through the folder that has subfolder and retrieve all files with extension '.element.ts' ?
const fs = require('fs');
const filesDir = fs.readdirSync('packages/web-components/src');
// the json result that will be generated
let content = [];
files.forEach(file => {
if (fileName === '???')
content.push(file);
});
const fs=require('fs');
function getAllFiles (dir, allFilesList = []){
const files = fs.readdirSync(dir);
files.map(file => {
const name = dir + '/' + file;
if (fs.statSync(name).isDirectory()) { // check if subdirectory is present
getAllFiles(name, allFilesList); // do recursive execution for subdirectory
} else {
allFilesList.push(name); // push filename into the array
}
})
return allFilesList;
}
const allFiles = getAllFiles('./testfolder');
const fileEndsWith = allFiles.filter(file => file.endsWith('.element.ts'))
console.log(fileEndsWith);
Easy way: use glob package.
const glob = require("glob");
const pattern = "packages/web-components/src/*.element.ts"
const elementTsFilenames = glob.sync(pattern);
Manual way:
const dir = "packages/web-components/src";
const extension = ".element.ts";
const elementTsFilenames = fs.readdirSync(dir).filter(fn => fn.endsWith(extension));
This is easy enough that the manual way is as easy or easier; if you have more complex requirements (e.g. recursively searching subdirectories), a library approach is nice.
I'll show you how to recursively get all the files in a directory (even those located in a subdirectory).
To do this, we need to create a recursive function that can call itself when dealing with sub-directories. And we also need the function to go through each of the sub-directories and add any new files it encounters. Then we also need to check if the filename contains a specific string. When the function is finished , it should return an array with all the files it encountered.
Here's what the recursive function looks like:
const fs = require("fs")
const path = require("path")
const getAllFiles = function(dirPath, extension, arrayOfFiles) {
files = fs.readdirSync(dirPath);
arrayOfFiles = arrayOfFiles || [];
files.forEach(function(file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles);
} else if (file.endsWith(extension)){
arrayOfFiles.push(path.join(__dirname, dirPath, "/", file));
}
});
return arrayOfFiles;
}
First, we require() the Node.js path module. Since this is
included with Node.js, you don't need to install anything for it to
work. This module will help us easily create full file paths for our
files.
The getAllFiles variable holds the recursive function that will go
through each subdirectory and return an array of filenames. It takes a
directory file path, a specified character and an optional arrayOfFiles as arguments.
Inside the getAllFiles function, we first use the readdirSync()
function to get all of the files and directories inside the given
dirPath supplied to the function.
Then, we create an arrayOfFiles that will hold all the filenames
that will be returned when the function is done running.
Next, we loop over each item (file or directory) found by the
readdirSync() function. If the item is a directory, we have the
function recursively call itself to get all of the files and
sub-directories inside the given directory.
And if the item is a file, we simply append the file path to the
arrayOfFiles array. (When the end of the file name is confirmed to
contain characters)
When the forEach loop has finished, we return the arrayOfFiles
array.
Here is how you use the function in your code:
const result = getAllFiles("packages/web-components/src", ".element.ts");
I don't think you need an npm package for this: It's not too hard to walk the file system using an async iterator and filter the results based on something like a regular expression.
Another bonus of an async technique is that it doesn't block your thread while it iterates the files (other work can be done in between each result while it's searching), especially if you have a lot of sub-directories/files to search through.
If you want to reduce your project's dependencies, you can do something like this:
example.mjs:
import * as path from 'node:path';
import {readdir} from 'node:fs/promises';
/** Search all subdirectories, yielding matching file entries */
export async function* findFiles (dir, regexpFilter) {
for (const entry of await readdir(dir, {withFileTypes: true})) {
const fPath = path.resolve(dir, entry.name);
if (entry.isDirectory()) {
yield* findFiles(fPath, regexpFilter);
continue;
}
if (regexpFilter && !regexpFilter.test(entry.name)) continue;
yield Object.assign(entry, {path: fPath});
}
}
async function main () {
const dir = 'packages/web-components/src';
// Regular expression which means: ends with '.element.ts'
const filter = /\.element\.ts$/;
for await (const entry of findFiles(dir, filter)) {
// ^^^^^^
// If you don't include a filter argument, then all files will be iterated
console.log(entry.name); // just the file name
console.log(entry.path); // the full file path
}
}
main();
I am attempting to do a file search based off array of file names and root directory. I found some file search snips online that seem to work when I am finding just 1 single file, but it will not work when there is more than 1 file specified. Below is the snippet:
const fs = require('fs');
const path = require('path');
var dir = '<SOME ROOT DIRECTORY>';
var file = 'Hello_World.txt'
var fileArr = ['Hello_World.txt', 'Hello_World2.txt', 'Hello_World3.txt'];
const findFile = function (dir, pattern) {
var results = [];
fs.readdirSync(dir).forEach(function (dirInner) {
dirInner = path.resolve(dir, dirInner);
var stat = fs.statSync(dirInner);
if (stat.isDirectory()) {
results = results.concat(findFile(dirInner, pattern));
}
if (stat.isFile() && dirInner.endsWith(pattern)) {
results.push(dirInner);
}
});
return results;
};
console.log(findFile(dir, file));
Any thoughts on how I can get this working with an array rather than just a single file string?
Doing the following seems to be working, but didn't know if there were other ways to do this which may be simpler:
newFileArr = [];
fileArr.forEach((file => {
//findFile(dir, file).toString();
console.log(findFile(dir, file).toString());
}));
The only thing that needs to change is the condition to determine if an individual filepath meets the search criteria. In the code you've posted, it looks like dirInner.endsWith(pattern), which says "does the filepath end with the given pattern?"
We want to change that to say "does the filepath end with any of the given patterns?" And closer to how the code will look, we can rephrase that as "Can we find a given pattern such that the filepath ends with that pattern?"
Let's rename pattern to patterns. Then we can simple replace dirInner.endsWith(patterns) with patterns.some(pattern => dirInner.endsWith(pattern))
I am writing a Webpack loader for Pug files. It loads all the dependent images referenced in the Pug file, uses file-loader to copy them to the appropriate directory, and replaces require() calls in the Pug file with the URLs of the resultant file.
I am trying to add functionality to allow for data URLs to be interpolated into the Pug file in lieu of file URLs. I am using this.loadModule() to execute file-loader now.
This function takes a callback as an argument. One of the args passed to this callback contains the result of url-loader, the data URL. So, I need this to write into the Pug file that should be output.
The problem is, the entire loader finishes running before that callback ever runs. So, anything I do with the data in there doesn't end up in the final file. The overall structure of my program looks like this:
module.exports = function pugDepLoader(inputFile) {
/* Setting up various constants and variables. */
/* A while() loop that continually tests inputFile with a
regex to find require() statements.
It creates a copy of inputFile called outputFile,
which it iterates over again and again,
replacing require() statements with file paths
one at a time.
The call to this.loadModule() is within this while() loop. */
/* End of while() loop */
/* Return outputFile to Webpack, which is now a
string with the Pug file, but with the
require() statements replaced with
file paths.
This is meant to be fed to file-loader to
get written out to disc. */
}
That return statement at the end will get called repeatedly before this.loadModule()'s callback ever runs once, as I have discovered with debugging statements. I need to be able to get that data URL that is provided within the callback into outputFile before I return it, so that it will end up in the file that is ultimately written to disc.
Entire source code follows:
module.exports = function pugDepLoader(inputFile) {
const fs = require('fs');
const path = require('path');
const loaderUtils = require('loader-utils');
var options = loaderUtils.getOptions(this);
//This option is required. It specifies
//the path to the root of the Pug file's
//dependencies relative to the location
//of the Pug files.
var contextualRoot;
if(!options || !options.hasOwnProperty('contextualRoot')) {
throw new Error('You must specify a contextual root');
}
else {
contextualRoot = options.contextualRoot;
//Ensure there is a trailing slash.
if(contextualRoot[contextualRoot.length-1] !== '/') {
contextualRoot = contextualRoot + '/';
}
}
//Determines whether paths should begin with
//a leading slash. Useful for Express.js
//compatibility.
if(!options.hasOwnProperty('absolute')) {
options.absolute = false;
}
//Set up regex to search for require() statements
const reqRE = new RegExp(/require\(/, 'g');
//outputFile will be returned containing the
//appropriately processed Pug
var outputFile = inputFile.slice();
//We need to execute reqRE once to kick things
//off, and we need to save it to a variable
//because we need information from it.
let regexResult = reqRE.exec(inputFile);
//regexResult will be null when there
//are no more matches to be found in
//the file.
while(regexResult != null) {
//pathStartIndex is the beginning of
//the path to the required file.
//We add 1 to skip the opening
//single quote.
let pathStartIndex = reqRE.lastIndex+1;
//We require the path to be wrapped in
//single quotes, so that we can easily
//be certain about where the require
//statement ends.
if(inputFile[reqRE.lastIndex] !== "'") {
console.log('FATAL ERROR: File path must be wrapped in single quotes!');
break;
}
//inputPath will hold the actual file path itself.
let inputPath = inputFile.slice(pathStartIndex, inputFile.indexOf("'", pathStartIndex));
//pathArray is used to split the
//path so we can easily extract
//the file name and path
//separately.
let pathArray = inputPath.split('/');
//Just the file name, with extension.
let fileName = pathArray.pop();
//outputPath will define what path should be
//written into the output Pug file.
//The user may optionally specify a
//custom output path.
let outputPath;
if(options.outputPath) {
outputPath = options.outputPath;
}
else {
outputPath = pathArray.join('/');
}
//Ensure a trailing slash.
if(outputPath[outputPath.length-1] !== '/') {
outputPath = (outputPath + '/');
}
//reqStart holds the index of the letter
//"r" in require(), so that we can remove
//the require() call and replace it
//with the file path.
let reqStart = inputFile.indexOf('require', regexResult.index);
//reqStmt is the require() statement in
//full. This will be used with replace()
//to replace the require() call with a
//file path in the output file.
let reqStmt = inputFile.slice(reqStart, inputFile.indexOf('"', pathStartIndex));
//The final file path, with file name.
//This will be written into the output
//Pug file in place of the require()
//calls.
let filePath = outputPath + fileName;
if(options.absolute && filePath[0] !== '/') {
if(filePath[0] === '.' && filePath[1] === '/') {
filePath = filePath.slice(1);
}
else {
filePath = '/' + filePath;
}
}
else if(!options.absolute && filePath[0] === '/') {
filePath = filePath.slice(1);
}
this.loadModule(contextualRoot + inputPath, (err, res, srcmap, module) => {
if(err) {
console.log(err);
}
if(new RegExp(/data:image/).test(res)) {
filePath = res.slice(res.indexOf('data:image'), res.lastIndexOf('"'));
}
});
//This takes care of require() calls
//inside of url() CSS functions,
//such as are used to declare
//background images inline.
//If the word "require" is
//preceeded by a single quote,
//it is within a url() function,
//and so we add appropriate closure
//for that function to the end of
//the path.
if(inputFile[reqStart-1] === "'") {
filePath = filePath + "');";
}
//Write the output.
outputFile = outputFile.replace(reqStmt, filePath);
//Run the next iteration of the regex search.
regexResult = reqRE.exec(inputFile);
}
//Return output as a string to Webpack.
return outputFile;
}
I am new to react-native development and using RNFetchBlob to work with files in internal storage, I wanted to create a gallery application and for that I need to fetch all the images present in the phone.
What i was able to do is fetch all files from a particular directory and search for images in it.
RNFetchBlob.fs.ls(RNFetchBlob.fs.dirs.DCIMDir).then((files) => {
//Returns all files present in the directory
console.log(files);
//Returns files with .png or .jpg extension.
console.log(files.find((element) => {return element.match('/(.png)$|(.jpg)$|(.jpeg)$/g')}));
}).catch((err) =>{
console.log(err);
});
But with this approach I need to search in every directory by iterating into them with recursion, I wanted to know if there is some way fetching all the image files just with one command.
We can have a recursive function something like below. I assume there must be some other optimized way but I ended up doing this. Besides we can cut off traversal inside directory if lastModified has not been changed.
async function findAllFilesByExtension(path = INTERNAL_STORAGE_PATH) {
let files = [];
const lsList = await RNFetchBlob.fs.lstat(path);
if (!!lsList && Array.isArray(lsList)) {
let dirs = [];
lsList.forEach(item => {
if (item.type === 'directory') {
dirs.push(item);
return;
}
if (item.type === 'file' && item.filename.match('/(.png)$|(.jpg)$|(.jpeg)$/g')) {
files.push(item)
return;
}
});
const response = await Promise.all(unmodifiedDirs.map(item=>findAllFilesByExtension(item.path)));
response.forEach(nestedFiles => {
files = [...files, ...nestedFiles];
});
}
return files;
}
I am working on creating my first Node.js module, and am having a difficult time trying to decide the best way to handle this. What is the recommended method for receiving paths to files to my module as input by the user? How can I then resolve where that is? Where the user called my module and the location of my running code are in two different places.
Let's take this a step further. Let's say a user has a JSON config file with an array of these paths (in config/settings.json):
{
"paths": [
"../path1",
"path2",
"/path/to/path3",
"~/path4"
]
}
Now obviously these paths are relative to the config file, not any executing code. This feels wrong, as now my method must accept the __dirname, the path of the config file relative to __dirname, etc.
var mymodule = require('mine');
mymodule(__dirname, './config/settings.json');
Any files I read from the config must then be based on the location of this config file. This feels weird. Suggestions?
From FileUtils source:
var updateFileProperties = function (file, path){
file._path = null;
file._usablePath = null;
file._isAbsolute = false;
if (path === undefined || path === null) return;
path = PATH.normalize (path);
var index = path.indexOf (":") + 1;
var windowsRoot = path.substring (0, index);
path = path.substring (index);
//https://github.com/joyent/node/issues/3066
if (path[0] === "/" && path[1] === "/"){
path = path.replace (/[\/]/g, "\\");
path = path.substring (0, path.length - 1);
}
file._isAbsolute = path[0] === SLASH;
file._path = windowsRoot + path;
file._usablePath = file._isAbsolute ? file._path : (windowsRoot + PATH.join (file._relative, path));
};
var File = function (path){
var main = process.mainModule.filename;
var cwd = main.substring (0, main.lastIndexOf (SLASH));
var relative = PATH.relative (process.cwd (), cwd);
this._relative = relative;
//...omitted
updateFileProperties (this, path);
};
This piece of code resolves the relative path problem.
_usablePath contains the real path. The "." path will be the directory where the main file is located. No matter how is called, it will point to the expected directory.
You can test the functions printing what the new File (<path>).getPath () returns.
I would avoid complexity and go for absolute paths only. Most users do __dirname + '../relativepath' anyways. ( at least that's what I do ).