defiler/API.md

156 lines
9.9 KiB
Markdown
Raw Permalink Normal View History

2017-04-24 12:23:28 +00:00
The API consists of two classes, `File` and `Defiler`.
2018-03-07 06:49:44 +00:00
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.
2017-04-24 12:23:28 +00:00
2018-03-09 14:48:55 +00:00
A `Defiler` represents a set of watched files on the disk, plus a set of virtual files, plus the operations to perform on them.
2017-04-24 12:23:28 +00:00
# `File`
## Constructor
2018-03-09 14:48:55 +00:00
### `new File()`
2017-04-24 12:23:28 +00:00
2018-03-09 14:48:55 +00:00
A new `File` instance to serve as the representation of a physical file, a virtual file, or a transformed file.
2017-04-24 12:23:28 +00:00
## Properties
### `path`
2018-03-09 14:48:55 +00:00
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.
2017-04-24 12:23:28 +00:00
2018-03-02 02:53:11 +00:00
### `dir`
2018-03-29 15:03:06 +00:00
The directory (not including the trailing slash) containing the file. For top-level files, this is an empty string.
2018-03-02 02:53:11 +00:00
### `filename`
The filename (including the extension) of the file.
2017-04-24 12:23:28 +00:00
### `ext`
2018-03-29 15:03:06 +00:00
The extension (including the preceding `.`) of the file. For extension-less files, this is an empty string.
2017-04-24 12:23:28 +00:00
2018-03-08 18:18:47 +00:00
### `stats`
The `fs.Stats` of the file.
2017-04-24 12:23:28 +00:00
### `bytes`
2018-03-10 19:33:08 +00:00
The file's contents can be retrieved and updated by getting or setting `bytes`, which is a `Buffer`.
2017-04-24 12:23:28 +00:00
2018-03-09 14:48:55 +00:00
Don't mutate this property. This causes various unwanted effects. Instead, assign it a new `Buffer` instance.
2017-04-24 12:23:28 +00:00
### `text`
2018-03-10 19:33:08 +00:00
The file's contents can also be retrieved and updated by getting or setting `text`, which is a string.
2018-02-26 19:13:45 +00:00
2018-03-09 14:48:55 +00:00
Reassigning the entire `bytes` or `text` properties will keep the other in sync.
2017-04-24 12:23:28 +00:00
2018-03-02 02:53:11 +00:00
### `enc`
2018-03-07 06:49:44 +00:00
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.
2018-03-02 02:53:11 +00:00
2017-04-24 12:23:28 +00:00
# `Defiler`
## Constructor
2018-04-17 01:50:48 +00:00
### `new Defiler({ dir, filter, read = true, enc = 'utf8', pre, watch = true, debounce = 10 }, ..., { transform, generators, resolver, onerror })`
2017-04-24 12:23:28 +00:00
2018-04-17 01:50:48 +00:00
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`.
2017-04-24 12:23:28 +00:00
2018-03-20 13:14:48 +00:00
- Input configuration
2018-03-29 15:03:06 +00:00
- `dir` - a directory to watch
2018-03-20 13:14:48 +00:00
- `filter({ 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 relative `path` and its `stats`. It should return `true` or `false` (or a `Promise` resolving to one of those). Returning `false` for a directory means that none of its contents will be included. You can use `stats.isFile()` and `stats.isDirectory()` to determine whether this is a file or a directory
2018-03-07 06:49:44 +00:00
- `read` - _(optional)_ whether to actually read in the contents of the files in the directory. Defaults to `true`. If `false`, the files will still be run through the transform, but they will have null `bytes` and `text`
2018-03-20 13:14:48 +00:00
- Can also be a function `read({ path, stats })`, which should return `true` or `false` (or a `Promise` resolving to one of those), allowing whether each file is read in to be decided individually
- `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 a `Promise` resolving to one), allowing the encoding on each file to be decided individually
2022-09-27 01:48:15 +00:00
- `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 containing `path` and `stats`. You can change the `path` value (perhaps adding a prefix) and can also add further custom fields that will exist on the `file` when it is passed to the `transform`. (It is this potentially modified `path` that will be used in [`defiler.get`](#getpath).) This allows you to (among other things) determine which directory a file came from when transforming it. The pre-processing function can return a `Promise` to indicate when it's done
2018-03-07 06:49:44 +00:00
- `watch` - _(optional)_ whether to actually watch the directory for changes. Defaults to `true`. If `false`, the files will still be run through the transform, but any changes to them will not be
2018-03-20 13:14:48 +00:00
- `debounce` - _(optional)_ length of timeout in milliseconds to use to debounce incoming events from `fs.watch`. Defaults to 10. Multiple events are often emitted for a single change, and events can also be emitted before `fs.stat` reports the changes. Defiler will wait until `debounce` milliseconds have passed since the last `fs.watch` event for a file before handling it. The default of 10ms Works On My Machine
2018-03-29 15:03:06 +00:00
- Transform/generator/resolver configuration
2018-04-24 17:22:17 +00:00
- `transform({ file, event })` - a transform function, which is passed an object containing the `File` instance to mutate and an `event` string indicating why this file is being run through the transform. This `event` can be `'read'` (indicating the file was just read in from the disk), `'add'` (indicating it was just manually added by calling [`defiler.add`](#addfile)), `'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 a `Promise` to indicate when it's done
- `generators` - _(optional)_ an array of generator functions, each of the form `generator()`. Each generator is called without arguments, and can return a `Promise` to indicate when it's done
2018-03-20 13:14:48 +00:00
- `resolver(base, path)` - _(optional)_ a function that will be used to resolve the paths passed to `defiler.get` and `defiler.add` from the transform. This will be passed two arguments, `base` (the path of the file being transformed) and `path` (the path passed to `defiler.get`/`defiler.add`), and should return the resolved path to use
2018-04-17 01:50:48 +00:00
- `onerror(error)` - _(optional)_ a function that will be called with an error object whenever an error occurs. See [Errors](#errors) below.
2017-04-24 12:23:28 +00:00
2018-03-07 06:49:44 +00:00
## Properties
2017-04-24 12:23:28 +00:00
2018-03-07 06:49:44 +00:00
### `paths`
2017-04-24 12:23:28 +00:00
2018-03-07 06:49:44 +00:00
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.
2017-04-24 12:23:28 +00:00
### `files`
2018-03-07 06:49:44 +00:00
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.
2017-04-24 12:23:28 +00:00
2018-03-09 11:27:49 +00:00
## Methods
2017-04-24 12:23:28 +00:00
2018-02-17 21:20:23 +00:00
### `exec()`
2017-04-24 12:23:28 +00:00
2018-03-09 14:48:55 +00:00
Start the `Defiler` running.
Returns a `Promise` that resolves when the initial wave of processing is complete.
2017-12-30 02:05:26 +00:00
2018-03-09 11:27:49 +00:00
### `get(path)`
2017-04-24 12:23:28 +00:00
2018-06-29 04:52:38 +00:00
Wait for a file to be ready and retrieve the `File` instance.
2017-04-24 12:23:28 +00:00
2018-06-24 10:47:47 +00:00
- `path` - the path to wait for to become available and to then return
2018-03-07 06:49:44 +00:00
2018-06-24 10:47:47 +00:00
Returns a `Promise` resolving to the `File` instance.
2017-04-24 12:23:28 +00:00
2018-06-24 10:47:47 +00:00
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.
2018-06-24 10:47:47 +00:00
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.
### `get(paths)`
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.
### `get(filter)`
2018-06-29 04:52:38 +00:00
Wait for all files whose paths match a given filter function and retrieve the `File` instances.
2018-06-24 10:47:47 +00:00
- `filter(path)` - a function that will be passed a path and should return a boolean
2018-06-29 04:52:38 +00:00
Returns a `Promise` resolving to an array of matching `File` instances, sorted by their (original) paths.
2018-06-24 10:47:47 +00:00
2018-06-29 04:52:38 +00:00
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.
2017-04-24 12:23:28 +00:00
2018-03-09 11:27:49 +00:00
### `add(file)`
2017-04-24 12:23:28 +00:00
2018-03-09 11:27:49 +00:00
Manually insert a virtual `File`, running it through the transform.
2018-01-30 11:47:33 +00:00
2018-03-30 18:50:27 +00:00
- `file` - the file data of the virtual file to add
2018-02-26 19:13:45 +00:00
2020-10-29 13:15:18 +00:00
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.)
2018-03-29 15:03:06 +00:00
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`.
2017-04-24 12:23:28 +00:00
2018-03-30 18:50:27 +00:00
### `resolve(path)`
Resolves a path from the file being transformed, using your specified `resolver`.
- `path` - the path to resolve
2018-06-24 10:47:47 +00:00
Returns the resolved path.
If you did not specify a `resolver` or if you are currently in a generator, this will be `path` unchanged.
2018-03-30 18:50:27 +00:00
2018-04-17 01:50:48 +00:00
## Errors
2017-04-24 12:23:28 +00:00
2018-04-17 01:50:48 +00:00
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.
2018-03-07 06:49:44 +00:00
2018-04-24 17:22:17 +00:00
### Transform errors: `{ file, event, error }`
2018-03-07 06:49:44 +00:00
2018-04-24 17:22:17 +00:00
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`.
2017-04-24 12:23:28 +00:00
2018-04-24 17:22:17 +00:00
### Generator errors: `{ generator, error }`
2017-04-24 12:23:28 +00:00
2018-04-24 17:22:17 +00:00
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`.