The API consists of two classes, File
and Defiler
.
A File
represents a physical file on the disk, or a virtual file with no particular corresponding file in the file system, or a partially or fully transformed physical or virtual file.
A Defiler
represents a set of watched files on the disk, plus a set of virtual files, plus the operations to perform on them.
A new File
instance to serve as the representation of a physical file, a virtual file, or a transformed file.
The relative path to the file, from some understood root. The path is always separated by forward slashes, regardless of platform. Updating dir
, filename
, or ext
also updates this.
The directory (not including the trailing slash) containing the file. For top-level files, this is an empty string.
The filename (including the extension) of the file.
The extension (including the preceding .
) of the file. For extension-less files, this is an empty string.
The fs.Stats
of the file.
The file's contents can be retrieved and updated by getting or setting bytes
, which is a Buffer
.
Don't mutate this property. This causes various unwanted effects. Instead, assign it a new Buffer
instance.
The file's contents can also be retrieved and updated by getting or setting text
, which is a string.
Reassigning the entire bytes
or text
properties will keep the other in sync.
The assumed encoding for the file. Defaults to 'utf8'
. Must be one of Node.js's supported encodings. Changing this in the middle of processing a file can cause confusing behavior, and is not recommended.
new Defiler({ dir, filter, read = true, enc = 'utf8', pre, watch = true, debounce = 10 }, ..., { transform, generators, resolver, onerror })
A new Defiler
instance to represent a collection of physical files and virtual files, a transform to run on them, and additional generators. This constructor should be passed multiple arguments; all but the last one should be 'input configuration' objects, and the last should be an object containing transform
and (optionally) generators
, resolver
and/or onerror
.
- Input configuration
dir
- a directory to watchfilter({ path, stats })
- (optional) a function to decide whether a given file or directory should be considered by Defiler. It's passed an object containing the file or directory's relativepath
and itsstats
. It should returntrue
orfalse
(or aPromise
resolving to one of those). Returningfalse
for a directory means that none of its contents will be included. You can usestats.isFile()
andstats.isDirectory()
to determine whether this is a file or a directory
read
- (optional) whether to actually read in the contents of the files in the directory. Defaults totrue
. Iffalse
, the files will still be run through the transform, but they will have nullbytes
andtext
- Can also be a function
read({ path, stats })
, which should returntrue
orfalse
(or aPromise
resolving to one of those), allowing whether each file is read in to be decided individually
- Can also be a function
enc
- (optional) encoding to use for files read in from the directory. Defaults to'utf8'
- Can also be a function
enc({ path, stats, bytes })
, which should return an encoding name (or aPromise
resolving to one), allowing the encoding on each file to be decided individually
- Can also be a function
pre(file)
- (optional) a function to run some very basic pre-processing specific to this directory before the file continues on to the common transform.file
is an object containingpath
andstats
. You can change thepath
value (perhaps adding a prefix) and can also add further custom fields that will exist on thefile
when it is passed to thetransform
. (It is this potentially modifiedpath
that will be used indefiler.get
.) This allows you to (among other things) determine which directory a file came from when transforming it. The pre-processing function can return aPromise
to indicate when it's donewatch
- (optional) whether to actually watch the directory for changes. Defaults totrue
. Iffalse
, the files will still be run through the transform, but any changes to them will not bedebounce
- (optional) length of timeout in milliseconds to use to debounce incoming events fromfs.watch
. Defaults to 10. Multiple events are often emitted for a single change, and events can also be emitted beforefs.stat
reports the changes. Defiler will wait untildebounce
milliseconds have passed since the lastfs.watch
event for a file before handling it. The default of 10ms Works On My Machine
- Transform/generator/resolver configuration
transform({ file, event })
- a transform function, which is passed an object containing theFile
instance to mutate and anevent
string indicating why this file is being run through the transform. Thisevent
can be'read'
(indicating the file was just read in from the disk),'add'
(indicating it was just manually added by callingdefiler.add
),'delete'
(indicating it's a file that was just deleted from the disk), or'retransform'
(indicating the file is unchanged but is being re-transformed because one of its dependencies changed). The transform function can return aPromise
to indicate when it's donegenerators
- (optional) an array of generator functions, each of the formgenerator()
. Each generator is called without arguments, and can return aPromise
to indicate when it's doneresolver(base, path)
- (optional) a function that will be used to resolve the paths passed todefiler.get
anddefiler.add
from the transform. This will be passed two arguments,base
(the path of the file being transformed) andpath
(the path passed todefiler.get
/defiler.add
), and should return the resolved path to useonerror(error)
- (optional) a function that will be called with an error object whenever an error occurs. See Errors below.
A Set
of the original relative paths of all of the physical files. (This does not include virtual files.) This will be available by the time your transform or generators are called, even if not all of the individual files have been read in yet.
A Map
of original relative paths to File
instances for the transformed files. (This includes physical and virtual files.) During the initial wave of processing, this will only contain the files that are done being transformed.
Start the Defiler
running.
Returns a Promise
that resolves when the initial wave of processing is complete.
Wait for a file to be ready and retrieve the File
instance.
path
- the path to wait for to become available and to then return
Returns a Promise
resolving to the File
instance.
This can be asked for a physical or virtual file. If you ask for a file during the initial wave of processing before it is available, Defiler will wait for the file to be ready and transformed. If it ever happens that every in-progress file is waiting for a file to become available, the deadlock will be broken by Defiler resolving all of the pending File
s to undefined
. This may happen multiple times during the initial wave of processing.
When used in your transform, this will also register the file being transformed as depending on the file at path
. Once the initial wave of processing is complete, any changes to dependencies will cause their dependents to be re-transformed. When used in a generator, this will register the generator as depending on the file at path
, and any changes to dependencies will cause the generator to be re-run.
Wait for multiple files to be ready and retrieve the File
instances.
paths
- the array of paths to wait for to become available and to then return
Returns a Promise
resolving to an array of File
instances.
Wait for all files whose paths match a given filter function and retrieve the File
instances.
filter(path)
- a function that will be passed a path and should return a boolean
Returns a Promise
resolving to an array of matching File
instances, sorted by their (original) paths.
This will return physical and virtual files. Once the initial wave of processing is complete, any new files matching the filter will also cause the generator or transform to be re-run.
Manually insert a virtual File
, running it through the transform.
file
- the file data of the virtual file to add
Returns a Promise
resolving when the file has been completely processed. (However, there is generally not a need to wait for this Promise
to resolve. Processing the added files will proceed in parallel, and dependence relationships between files will be maintained.)
The object does not need to be a File
instance, and in fact there is no benefit to doing so. A new File
instance is always created with properties Object.assign
ed from file
.
Resolves a path from the file being transformed, using your specified resolver
.
path
- the path to resolve
Returns the resolved path.
If you did not specify a resolver
or if you are currently in a generator, this will be path
unchanged.
The object passed to your onerror
callback will be of two forms, depending on whether it is the result of an error thrown by the transform or an error thrown by a generator.
When an error occurs in the transform, onerror
is called with an object containing the File
instance that caused the error, the event
that was passed to the transform, and the thrown error
.
When an error occurs in a generator, onerror
is called with an object containing the generator
function that threw the error and the thrown error
.