Product
Socket Now Supports uv.lock Files
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Phylo (pronounced "File-o") is a File operations class designed for maximum
convenience and clarity of expression. The primary export of phylo
is the
File
class which is used to wrap a file-system path string.
Consider some examples:
The root
value is determined by looking for a directory with a '.git'
file or
folder in it, starting at cwd
and climbing up as necessary. When that location is
found, it is returned as a File
object. Note, this is not the '.git'
folder
itself, but the folder that contains the '.git'
folder (that is, the VCS root).
The pkgFile
value is determined in a similar manner but with two differences. The
first is that the location for which we are searching must contain a file (not
a folder) with the name 'package.json'
. Secondly, it is the 'package.json'
file that is returned as a File
instance, not the location that contained it.
The load()
method will read the file and parse the contents into an object (since
the file is type '.json'
).
If you like infinite loops, you can try this on Windows:
var path = require('path');
for (var d = process.cwd(); d; d = path.resolve(d, '..')) {
// climb up...
}
This innocent loop works on Linux and Mac OS X because path.resolve('/', '..')
returns a falsy value. On Windows, however, path.resolve('C:\\', '..')
returns...
well 'C:\\'
!
Compare the above to the same idea using File
:
for (var d = File.cwd(); d; d = d.parent) {
// climb up...
}
The File
API strives to be purely consistent on these points:
String
or File
instances.Path
return a String
. Otherwise they return a File
instance (when paths are involved).null
return values are avoided. For
example, stat()
returns an object in all cases but that object may have an
error
property.Object.freeze()
enforced) instances and reused as
needed.The conflict between Node.js path
and fs
API's is a major reason for these
naming conventions. Consider:
let s = path.join(process.cwd(), 'foo'); // sync
fs.mkdir(s); // async!
Using File
:
let f = File.cwd().join('foo'); // sync (of course);
f.mkdir(); // also sync
f.asyncMkdir().then(... // async obviously
It is intended that a File
instance immutably describes a single path. What is
(or is not) on disk at that location can change of course, but the description is
constant.
Much of the functionality provided by the File
class is in the form of "lexical"
path manipulation. These are only provided in synchronous form since they operate on
path strings (like the path
module).
Instances of File
provide these readonly properties:
path
- The path to the file as a String
(passed to the constructor
).extent
- The file's type as a String
(e.g., 'json'
).name
- The file's name as a String
(e.g., 'package.json'
).parent
- The File
for the parent directory (null
at root).fspath
- The path
string resolved for '~'
(usable by fs
or path
modules)The methods that perform work on the path text and return File
instances as a
result are:
absolutify()
- Calls path.resolve(this.path)
join()
- Joins all arguments using path.join()
nativize()
- Make all separators native ('\'
on Windows, '/'
elsewhere)normalize()
- Calls path.normalize(this.path)
relativize()
- Calls path.relative()
resolve()
- Calls path.resolve()
on all the argumentsslashify()
- Make all separators '/'
(Windows does understand them)terminate()
- Ensure there is a trailing separatorunterminate()
- Ensure there is no trailing separatorTo retrieve strings as a result, you can use these methods:
absolutePath()
- Same as absolutify
but returns a stringjoinPath()
- Same as join
but returns a stringnativePath()
- Same as nativize
but returns a stringnormalizedPath()
- Same as normalize
but returns a stringrelativePath()
- Same as relativize
but returns a stringresolvePath()
- Same as resolve
but returns a stringslashifiedPath()
- Same as slashify
but returns a stringterminatedPath()
- Same as terminate
but returns a stringunterminatedPath()
- Same as unterminate
but returns a stringSome path operations perform I/O to the file-system and so provide both synchronous and asynchronous versions.
canonicalize()
- Calls fs.realpathSync(this.path)
and returns a File
canonicalPath()
- Same as canonicalize
but returns a String
In asynchronous form:
asyncCanonicalize()
- Same as canonicalize
but Promises a File
asyncCanonicalPath()
- Same as asyncCanonicalize
but Promises a String
Canonicalization will result in null
if there is no real file.
Some useful information about a file path:
isAbsolute()
- Returns true
if the file an absolute path (path.isAbsolute()
)isRelative()
- Returns true
if the file a relative path (path.isRelative()
)You can compare two paths in a few different ways:
compare(o,first)
- Returns -1, 0 or 1 if this
is less-than, equal or
greater-than o
. By default, directories sort before files (first = 'd'
). To
instead group files before directories, pass 'f'
. To compare only paths, pass
false
.
in which case files sort before directories.equals(o)
- Returns true
if this
is equal to o
(compare(o) === 0
)prefixes(o)
- Returns true
if this
is a path prefix of o
. It is
recommended to use absolutify()
on both instances first to avoid confusion with
..
segments.There are some static sort methods that can be used by Array.sort()
:
File.sorter
- Calls f1.compare(f2, 'd')
to group directories before files.File.sorterFilesFirst
- Calls f1.compare(f2, 'f')
to group files first.File.sorterByPath
- Calls f1.compare(f2, false)
to sort only by path.File name comparisons are case-insensitive on Windows and Mac OS X, so we have
var f1 = File.from('abc');
var f2 = File.from('ABC');
console.log(f1.equals(f2));
> true (on Windows and Mac)
> false (on Linux)
To get information about the file on disk (synchronously):
access()
- Returns a File.Access
object. If the file does not exist (or some
other error is encountered), this object will have an error
property.can(mode)
- Returns true
if this exists with the desired access (mode
is 'r'
,
'rw'
, 'rwx'
, 'w'
, 'wx'
or 'x'
).exists()
- Returns true
if the file exists.has(rel)
- Returns true
if a file or folder exists at the rel
path from this file.hasDir(rel)
- Returns true
if a folder exists at the rel
path from this file.hasFile(rel)
- Returns true
if a file exists at the rel
path from this file.isHidden()
- Returns true
if this file does not exist or is hidden. Note that on
Windows, hidden state is not based on a file name convention (".hidden") but is a bit
stored in the file-system (see below).stat()
/ restat()
- Returns fs.statSync(this.path)
(an fs.Stats
). If the
file does not exist (or some other error is encountered), this object will have an
error
property.statLink()
/ restatLink()
- Returns fs.lstatSync(this.path)
(an fs.Stats
). If
the file does not exist (or some other error is encountered), this object will have an
error
property.The error
property will be a value like 'ENOENT'
(for file/folder not found), and
'EACCES'
or 'EPERM'
for permission denied. These codes come directly from the
underlying API.
In asynchronous form:
asyncAccess()
- Promises a File.Access
asyncCan(mode)
- Promises true
or false
.asyncExists()
- Promises true
or false
.asyncHas(rel)
- TODOasyncHasDir(rel)
- TODOasyncHasFile(rel)
- TODOasyncIsHidden()
- Promises true
or false
asyncStat()
/ asyncRestat()
- Promises an fs.Stats
via fs.stat()
asyncStatLink()
/ asyncRestatLink()
- Promises an fs.Stats
via fs.lstat()
The fs.Stat
structure is augmented
with an attrib
property. This is an instance of File.Attribute
and will have these
boolean properties:
A
- ArchiveC
- CompressedE
- EncryptedH
- HiddenO
- OfflineR
- ReadonlyS
- SystemFor example:
if (File.cwd().join('package.json').stat().attrib.H) {
// If the package.json file is hidden... (wat?)
}
Note, if there is no 'package.json'
file, the stat()
method will return an object
with an error
property and an empty attrib
object (it won't have H
set).
The fswin
module is used to retrieve this
information on Windows. On other platforms, this object contains false
values for all
of the above properties.
An fs.Stat
object is cached on the File
instance by the stat()
family of methods
and a separate instance is cached on by the statLink()
family. These are lazily
retrieved and then stored for future use. To get fresh copies, use the restat()
family
of methods.
File.Access
objects are descriptors of read, write and execute permission masks.
These are much simpler to use than the fs.constants.R_OK
, fs.constants.W_OK
and
fs.constants.X_OK
bit-masks. For example:
try {
let mode = fs.statSync(file).mode;
if (mode & fs.constants.R_OK && mode & fs.constants.W_OK) {
// file exists and is R and W
}
}
catch (e) {
// ignore... file does not exist
}
Or using File
and File.Access
:
if (file.access().rw) {
// file exists and is R & W
}
Alternatively, there is the can()
method:
if (file.can('rw')) {
// file exists and is R & W
}
When the file does not exist, or an error is encountered, the object returned by the
access()
method will have an error
property. Since the access bits are all false
in this case, this distinction if often unimportant (as above).
To check for errors:
var acc = file.accecss();
if (acc.rw) {
// file exists and has R/W access
}
else if (acc.error === 'ENOENT') {
// file does not exist...
}
else if (acc.error === 'EACCES' || acc.error === 'EPERM') {
// access or permission error...
}
...
There are a fixed set of immutable File.Access
objects, one for each combination of
R, W and X permissions: r
, rw
, rx
, rwx
, w
, wx
, x
. Each instance also has
these same properties as boolean values. The full set of properties is a bit larger:
r
- True if R_OK
is set.rw
- True if R_OK
and W_OK
are both set.rx
- True if R_OK
and X_OK
are both set.rwx
- True if R_OK
, W_OK
and X_OK
are all set.w
- True if W_OK
is set.wx
- True if W_OK
and X_OK
are both set.x
- True if X_OK
is set.mask
- The combination of fs.constants
flags R_OK
, W_OK
and/or X_OK
name
- The string 'r'
, 'rw'
, 'rx'
, 'rwx'
, 'w'
, 'wx'
or 'x'
It is often important to know if a file is a directory or other type of entity. This
information is fundamentally the business of the stat()
family but for convenience is
also provided on the File
instance:
isDirectory(mode)
isFile(mode)
isBlockDevice(mode)
isCharacterDevice(mode)
isFIFO(mode)
isSocket(mode)
isSymbolicLink(mode)
In addition, the following shorthand methods are also available:
isDir(mode)
(alias for isDirectory()
)isSymLink(mode)
(alias for isSymbolicLink()
)These are also available as async methods:
asyncIsDir(mode)
asyncIsDirectory(mode)
asyncIsFile(mode)
asyncIsBlockDevice(mode)
asyncIsCharacterDevice(mode)
asyncIsFIFO(mode)
asyncIsSocket(mode)
asyncIsSymLink(mode)
asyncIsSymbolicLink(mode)
The optional mode
parameter can be 'l'
(lowercase-L) to use the statLink()
(or
asyncStatLink()
) method to determine the result.
Since the nature of a file seldom changes on a whim, these methods use the stat()
methods and their cached information. If this is undesired, these results can be
refreshed using the restat()
family of methods.
You can get a directory listing of File
objects using:
list(mode, matcher)
asyncList(mode, matcher)
The mode
parameter is a string that consists of the following single letter codes
with the described meaning:
A
- All files are listed, even hidden files. (default is false
)d
- List only directories. (default is false
)f
- List only files (non-directories). (default is false
)l
- Cache the result of statLink
for each file. (default is false
)o
- Order the items by sorter
. (default is true
)O
- Order the items by sorterFilesFirst
. (default is false
)s
- Cache the result of stat
for each file. (default is false
)w
- Indicates that Windows hidden flag alone determines hidden status
(default is false
so that files names starting with dots are hidden on all
platforms).T
- Throw (or reject) on failure instead of returning (or resolving) null
.Some examples:
// List non-hidden files/folders:
dir.list();
// lists all files/folders (including hidden):
dir.list('A');
// lists non-hidden files/folders and cache stat info:
dir.list('s');
// lists all files (no folders) and cache stat info:
dir.list('Asf');
// lists all files/folders and cache stat info but do not sort:
dir.list('As-o');
The s
option can be useful during an asyncList()
operation to allow subsequent
use of the simpler, synchronous stat()
method since it will use the cached stat
object.
The matcher
can be a function to call for each candidate. This function receives
the arguments (name, file)
. For example:
dir.list(name => {
return name.endsWith('.txt');
});
dir.list((name, f) => {
return f.extent === 'txt'; // f is a File instance
});
The matcher
can also be a RegExp
:
dir.list(/\.txt$/i);
Lastly, matcher
can be a "glob" (a shell-like wildcard). In this case, since this
is also a string, the mode
must be passed first:
dir.list('Af', '*.txt');
The basic form of globs is a file name and extension pattern (like '*.txt'
). The '*'
character matches only file name characters and not path separators ('/'
and '\'
on
Windows).
Internally globs are converted into RegExp
objects. The conversion of '*.txt'
is
platform-specific. For Linux, it is:
/^[^/]*\.txt$/
On Windows, it converts to this:
/^[^\\/]*\.txt$/i
This is because Windows uses either '/'
and '\'
as path separators and filenames
are case-insensitive.
To match paths, you can use a "glob star" such as '**/*.txt'
. This glob converts to
this on Linux:
/^(?:[^/]*(?:[/]|$))*[^/]*\.txt$/
Globs also support groups inside '{'
and '}'
such as: '*.{txt,js}'
:
/^[^/]*\.(txt|js)$/
A character set like '*.{txt,js}[abc]'
converts to:
/^[^/]*\.(txt|js)[abc]$/
The glob parser has some advanced options via the File.glob()
method. The File.glob()
method converts a glob string into a RegExp
. This conversion can be customized using
the second argument as the options
. This string can contain any of these characters:
C
- Case-sensitivity is manual (disables auto-detection by platform)G
- Greedy '*'
expansion changes '*'
to match path separators (i.e., '/'
)S
- Simple pattern mode (disables grouping and character sets)All other characters are passed as the RegExp
flags (e.g., 'i'
and 'g'
).
The 'S'
options enables "simple" glob mode which disables groups and character sets.
For example:
dir.list(File.glob('*.{txt,js}', 'S'));
== /^[^/]*\.{txt\,js}$/
This would be useful when dealing with files that have '{'
in their name.
To force case-sensitive comparison (e.g., on Windows):
let re = File.glob('*.txt', 'C');
/^[^\\/]*\.txt$/
To force case-insensitive comparison (e.g., on Linux), you need to use 'C'
to make
this a manual choice, and 'i'
to make the RegExp
ignore case:
let re = File.glob('*.txt', 'Ci');
/^[^/]*\.txt$/i
To climb the file-system to find a parent folder that passes a test
function or
has a particular file or folder relatively locatable from there:
up(test)
- Starting at this, climb until test
passes.upDir(rel)
- Use up()
with hasDir(rel)
as the test
.upFile(rel)
- Use up()
with hasFile(rel)
as the test
.To climb the file-system and find a relatively locatable item:
upTo(rel)
- Starting at this, climb until has(rel)
is true
and then return
join(rel)
from that location.upToDir(rel)
- Same as upTo()
but using hasDir(rel)
as the test
.upToFile(rel)
- Same as upTo()
but using hasFile(rel)
as the test
.The different between these forms can be seen best by example:
var file = File.cwd().up('.git');
// file is the parent directory that has '.git', not the '.git'
// folder itself. The file may be File.cwd() or some parent.
var git = File.cwd().upTo('.git');
// git is the '.git' folder from perhaps File.cwd() or some other
// parent folder.
Asynchronous forms (TODO - not implemented yet):
asyncUp(test)
- TODOasyncUpDir(rel)
- TODOasyncUpFile(rel)
- TODOasyncUpTo(rel)
- TODOasyncUpToDir(rel)
- TODOasyncUpToFile(rel)
- TODOtips(mode, test)
- Returns a File[]
of the top-most items passing the test
.
Once a match is found, no descent into that folder is made (hence, the "tips" of
the sub-tree). Uses walk(mode)
to descend the file-system.walk(mode, matcher, before, after)
- Calls before
for all items that
list(mode, matcher)
generates recursively, then processes those items and
lastly calls after
. Both before
and after
are optional but one
should be provided.The walk
method's before
and after
handlers looks like this:
function beforeOrAfter (file, state) {
if (file.isDir() && ...) {
return false; // do not recurse into file (before only)
}
if (...) {
state.stop = true; // stop all further walking
}
}
The optional matcher
can be a String
or a RegExp
and have the same meaning
as with list()
. The matcher
cannot, however, be a function. This is because it
would be ambiguous with before
and would really offer no advantage over handling
things in the before
method anyway.
The state
object has the following members:
at
- The current File
being processed.root
- The File
used to start the descent.stack
- A File[]
of instances starting with the File
used to start things.stop
- A boolean property that can be set to true
to abort the walk
.The tips
method's test
looks like this:
function test (file, state) {
if (file.hasFile('package.json')) {
return true; // file is a tip so gather it up and don't descend
}
return false; // keep going and/or descending
}
The state
parameter is the same as for the walk
method.
Asynchronous forms:
asyncTips(mode, test)
asyncWalk(mode, matcher, before, after)
The test
, before
and after
handlers of the asynchronous methods
accept the same parameters and return the same results as with the
synchronous forms. They can, alternatively, return a Promise if their
determination is also async.
Basic file reading and decoding/parsing are provided by these methods:
asyncLoad(options)
- Same as load()
except a Promise is returned.load(options)
- Reads, decodes and parses the file according to
the provided options
.And serializing, encoding and writing is provided by:
asyncSave(data, options)
- Same as save()
except a Promise is
returned.save(data, options)
- Serializes and encodes the data and writes
it to this file using the specified options
.The act of loading a file consists initially of reading the data (obviously). To
get this part right, you need an encoding
option which is tedious to setup in
the fs
API, especially if the file name holds the clues you need.
Compare:
var pkg = path.join(dir, 'package.json');
var data = JSON.parse(fs.readfileSync(pkg, 'utf8'));
To loading using File
:
var pkg = dir.join('package.json');
var data = pkg.load();
The basic advantage of the File
approach is the error messages you get when
things go wrong. Using the first snippet you would get errors like these (based
on the parser used):
Unexpected number in JSON at position 427
Using load()
the message would be:
Cannot parse ~/code/package.json: Unexpected number in JSON at position 427
With File
there is hope in tracking down what has gone wrong.
When it is time to save the data, the process looks very symmetric:
pkg.save(data);
Instead of the manual alternative:
fs.writeFileSync(pkg, JSON.stringify(data, null, ' '), 'utf8');
NOTE: Unlike most of the File
API, these methods throw exceptions (or reject
Promises) on failure.
Readers are objects that manage options for reading and parsing files. The following readers come predefined:
bin
- An alias for binary
.binary
- Reads a file as a buffer.json
- Extends the text
reader and provides a parse
method to
deserialize JSON data. This uses the json5
module to tolerate human friendly JSON.json:strict
- Extends text
reader and uses strict JSON.parse()
.text
- Reads a file as utf8
encoding.txt
- An alias for text
.Writers are objects that manage options for serializing and writing files. The following writers come predefined:
bin
- An alias for binary
.binary
- Writes a file from a buffer.json
- Extends the text
writer and provides a serialize
method to
write JSON data.json5
- Extends json
writer and uses json5.stringify()
.text
- Writes a file as utf8
encoding. Accepts a join
string option to
join the data if the data is an array (of lines perhaps).txt
- An alias for text
.The default reader is selected based on the file's type, but we can override this:
var data = pkg.load('text'); // load as a simple text (not parsed)
Other options can be specified (e.g. to split by new-line):
var data = pkg.load({
type: 'text',
split: /\n/g
});
Readers support the following configuration properties:
parse
- A function called to parse the file content. The method accepts two
arguments: data
and reader
. The data
parameter is the file's content and
the reader
is the fully configured reader
instance.split
- An optional RegExp
or String
for a call to String.split()
. This
is used by the default parse
method.In addition to reader
configuration, the fs.readFile()
options can be supplied:
var content = file.load({
// The options object is passed directly to fs.readFile()
options: {
...
}
});
The encoding
can be specified in the options
or directly to the reader
:
var content = file.load({
encoding: 'utf16'
});
// Or on the fs options:
var content = file.load({
options: {
encoding: 'utf16'
}
});
The default writer is selected based on the file's type, but we can override this:
pkg.save(data, 'text');
Other options can be specified (e.g. to join lines in an array with new-lines):
pkg.save(data, {
type: 'text',
join: '\n'
});
Writers support the following configuration properties:
serialize
- A function called to convert the data and return what will be written
to disk. The method accepts two arguments: data
and writer
. The data
parameter
is the raw file data and the writer
is the fully configured writer
instance.join
- An optional String
for a call to Array.join()
when file data is
an array. This is used by the default serialize
method.The json
writer also supports these properties:
indent
maps to the space
parameter for JSON.stringify
.replacer
map to the replacer
parameter for JSON.stringify
.In addition to writer
configuration, the fs.writeFile()
options can be supplied:
file.save(data, {
// The options object is passed directly to fs.writeFile()
options: {
...
}
});
The encoding
can be specified in the options
or directly to the writer
:
file.save(data, {
encoding: 'utf16'
});
// Or on the fs options:
file.save(data, {
options: {
encoding: 'utf16'
}
});
To remove a file or empty folder, you can use remove()
:
file.remove();
Internally, remove()
calls either fs.unlinkSync()
or fs.rmdirSync()
.
A folder tree can be removed by passing the 'r'
option:
dir.remove('r');
This will synchronously remove all children of dir
and then remove dir
itself.
Internally, this is handled by rimraf
.
The asynchronous form of remove()
is:
dir.asyncRemove().then(() => {
// dir is gone if it was empty
});
Or:
dir.asyncRemove('r').then(() => {
// dir and its children are gone
});
The most useful static methods are for conversion.
var file = File.from(dir);
Regardless if the value of dir
above is a String
or File
, file
is a File
instance. If dir
is null
or ''
then file
will be null
.
In reverse:
var s = File.path(file);
The path()
method accepts String
or File
and returns the path (the original
string or the path
property of the File
). Similar to from()
, the path()
method
returns ''
when passed null
. That value is still falsy but won't throw null
reference errors if used.
There is also fspath()
that resolves '~'
path elements:
var s = File.fspath(file);
If the argument is already a String
it is simply returned (just like the path()
method). If the string may contain '~'
elements, the safe conversion would be:
var s = File.from(file).fspath;
access(fs)
- Returns a FileAccess
for the File
or String
.exists(fs)
- Returns true if the File
or String
exists.isDir(fs)
- Returns true if the File
or String
is an existing directory.isFile(fs)
- Returns true if the File
or String
is an existing file.join(fs...)
- Return path.join()
on the File
or String
args as a File
.joinPath(fs...)
- Return path.join()
on the File
or String
args as a String
.resolve(fs...)
- Return path.resolve()
on the File
or String
args as a File
.resolvePath(fs...)
- Return path.resolve()
on the File
or String
args as a String
.split(fs)
- Returns a String[]
from the File
or String
.stat(fs)
- Returns the stat()
for the File
or String
.sorter(fs1, fs2)
- Calls File.from(fs1).compare(fs2)
(useful for sorting
File[]
and String[]
).There are no asynchronous forms of these utility methods since they wouldn't really save much:
Since this is not provided:
File.asyncExists(file).then(exists => {
...
});
Instead just do this:
File.from(file).asyncExists().then(exists => {
...
});
cwd()
- Wraps process.cwd()
as a File
.home()
- Wraps os.homedir()
as a File
.profile()
- Returns the platform-favored storage folder for app data.temp()
/ asyncTemp()
- Temporary folder for this application.which(name,opts)
- Searches the PATH for a program by name. The opts
can be
a replacement PATH or an object with a path
property that is the replacement.
The asynchronous form is asyncWhich(name,opts)
.The temp()
and asyncTemp()
static methods use the tmp
module to generate a temporary folder in the appropriate location for the platform.
When these methods are called with no options
argument, they lazily
create (and cache for future requests) a single temporary folder.
The profile()
method handles the various OS preferences for storing application
data.
C:\Users\Name\AppData\Roaming\Company
/Users/Name/Library/Application Support/Company
/home/name/.local/share/data/company
/home/name/.company
The "Company" part can be passed as the argument to profile()
but is better left to
the top-level application to set File.COMPANY
.
File.COMPANY = 'Acme';
Now all libraries that use phylo
will automatically store their profile data in the
proper folder for the user-facing application. In such a scenario it would be wise to
use the module name in the filename to ensure no collisions occur.
A common pseudo-root folder for the user's home folder is '~'
. One often sees
paths like this:
var dir = new File('~/.acme');
The '~'
pseudo-root is recognized throughout File
methods. It is resolved to the
actual location using absolutify()
or canonicalize()
(or their other flavors). In
other cases the pseudo-root is preserved. For example:
var dir = new File('~/.acme');
console.log(dir.parent); // just '~'
console.log(dir.join('foo')); // ~/acme/foo
These File
instances can be read using load()
as well:
var data = File.from('~/.acme/settings.json').load();
In addition there is also the '~~/'
pseudo-root that maps the the profile()
directory
instead of the raw homedir.
That is:
File.COMPANY = 'Acme';
console.log(File.from('~/foo').absolutePath());
console.log(File.from('~~/foo').absolutePath());
// Windows:
> C:\Users\MyName\foo
> C:\Users\MyName\AppData\Roaming\Acme\foo
// Mac OS X:
> /Users/MyName/foo
> /Users/MyName/Library/Application Support/foo
The which()
method can be used like the standard which
shell command:
let nodejs = File.which('node');
If the program is not found, null
is returned. Other problems will result in an thrown
Error
.
The asynchronous form is similar:
File.asyncWhich('node').then(nodejs => {
if (nodejs) {
// found it
}
else {
// not found
}
},
e => {
// some error
});
You can also customize the search to your own list of directories:
let app = File.which('app', [ '~/bin', '.' ]);
You can create a directory structure using the mkdir()
method (or asyncMkdir()
).
These methods create as many directory levels as needed to create the path described
by the File
instance.
var dir = File.from('~~/foo').mkdir();
The mkdir()
method returns the File
instance after creating the directory tree.
Unlike many other File
methods, if mkdir()
fails it will throw an Error
.
Phylo owes many thanks to the following modules and, of course, their authors and maintainers (in alphabetic order):
For some opinions on when to use async methods see here.
Enjoy!
Copyright (c) 2016 Donald Griffin
FAQs
File operations class
We found that phylo demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.