Configuration
Image Processing Pipeline may be configured by using any of the file naming schemes supported by cosmiconfig.
A custom configuration file may also be specified using the --config
flag, which will override the
default searching behaviour and instead exclusively attempt to use the provided config path.
The loaded configuration will be validated using a JSON schema to check for inconsistencies and warn you of any problems.
Searching
If no configuration is explicitly specified via the --config
flag, IPP will attempt to search the
current working directory and parent directories for a recognised configuration file. Any file
supported by cosmiconfig will be correctly understood by IPP.
For example, you may define one of the following files (in order of priority):
- an ipp property in your package.json
- a JSON or YAML .ipprc file
- a JSON .ipprc.json, YAML .ipprc.yml or JavaScript ipprc.js file
- a JavaScript .config.js file
Choosing a format
IPP eventually converts configurations to JS, but understands several base formats. Each have slight differences, with more abstraction towards the top of the list.
- YAML is the most human-friendly and compact (recommended for most users)
- JSON is a widely recognised universal format, similar to JS
- JS allows for dynamic configs and lambda pipe functions
Programmatic invocation
Calling the CLI's startCLI()
function programmatically bypasses any configuration searching or
validation. Instead, the configuration is passed as a function parameter.
Due to the dynamic nature of JavaScript, it is your responsibility to ensure that the configuration is correct, an incorrect configuration could cause undetermined execution behaviour.
If you are using TypeScript, you can rest easy knowing that the function is strongly typed, providing the same level of safety against faulty configuration, if not better.
Validation
Upon loading a configuration, it is converted to JavaScript and validated against this JSON schema. This strict behaviour ensures that there are no misspelt property names or deprecated keys.
tip
If you save your configuration in the JSON format, you may benefit from intelligent JSON Schema
features offered by your IDE by specifying a root $schema
property that points to this permanent
schema URL.
{
"$schema": "https://ipp.vercel.app/schema/config.json",
...
}
Example configuration
Below is a good starting point for building a set of responsive website images. Give it a try and then adjust it to suit your needs.
In order to use the following configuration, you will also need to install the @ipp/compress and @ipp/primitive packages.
$ npm install --save-dev @ipp/compress @ipp/primitive
- YAML
- JSON
input: images/
output: build/static
manifest:
source:
x: source.hash:8
format:
w: width
h: height
pipeline:
- pipe:
resolve: "@ipp/primitive"
module: PrimitivePipe
save: "[source.name]-ptv-[hash:8][ext]"
- pipe: convert
options:
format: raw
then:
- pipe: resize
options:
breakpoints:
- name: sm
resizeOptions:
width: 640
- name: md
resizeOptions:
width: 1280
- name: lg
resizeOptions:
width: 1920
- name: xl
resizeOptions:
width: 3840
then:
- pipe: convert
options:
format: original
convertOptions:
quality: 100
then:
- pipe:
resolve: "@ipp/compress"
module: CompressPipe
options:
softFail: true
save: "[source.name]-[breakpoint]-[hash:8][ext]"
- pipe: convert
options:
format: webp
save: "[source.name]-[breakpoint]-[hash:8][ext]"
{
"input": "images/",
"output": "build/static",
"manifest": {
"source": {
"x": "source.hash:8"
},
"format": {
"w": "width",
"h": "height"
}
},
"pipeline": [
{
"pipe": {
"resolve": "@ipp/primitive",
"module": "PrimitivePipe"
},
"save": "[source.name]-ptv-[hash:8][ext]"
},
{
"pipe": "convert",
"options": {
"format": "raw"
},
"then": [
{
"pipe": "resize",
"options": {
"breakpoints": [
{
"name": "sm",
"resizeOptions": {
"width": 640
}
},
{
"name": "md",
"resizeOptions": {
"width": 1280
}
},
{
"name": "lg",
"resizeOptions": {
"width": 1920
}
},
{
"name": "xl",
"resizeOptions": {
"width": 3840
}
}
]
},
"then": [
{
"pipe": "convert",
"options": {
"format": "original",
"convertOptions": {
"quality": 100
}
},
"then": [
{
"pipe": {
"resolve": "@ipp/compress",
"module": "CompressPipe"
},
"options": {
"softFail": true
},
"save": "[source.name]-[breakpoint]-[hash:8][ext]"
}
]
},
{
"pipe": "convert",
"options": {
"format": "webp"
},
"save": "[source.name]-[breakpoint]-[hash:8][ext]"
}
]
}
]
}
]
}
Breaking it down
This configuration will do things. Firstly, it will generate four different sizes, labelled sm, md, lg and xl, saving them in the original format and as a WebP image, and secondly, create an SVG placeholder using the primitive pipe.
Every exported format is then saved to the manifest with the original image's hash for easy lookup, and the exported format's image dimensions.
Processing in raw pixel data
In this example, we first convert the image to raw pixel data and use that to resize the image and convert back to lossy formats, such as JPEG and WebP.
This will yield slightly better results (especially for the WebP image, as it has a better source),
at the cost of using more memory during image processing. If your system doesn't have enough RAM,
consider lowering the concurrency or removing the raw and original convert pipes and removing
the resize pipe's quality: 100
option.
tip
All of IPP's built-in and official pipes support raw pixel data.
tip
The WebP export does not need a compression pipe, there aren't any good compression algorithms that beat the reference codec encoder.
API reference
To get a more standardised and always up-to-date API reference, check out the configuration JSON schema or TypeScript interfaces.
- Table
- TypeScript
key | type | description |
---|---|---|
input | string or array | The path to an image, or a folder containing images (required) |
output | string | The path to the directory to output images to (required) |
pipeline | array | See the pipeline (required) |
concurrency | number | Allows for more fine-grain control of the program options |
clean | boolean | Delete the output directory before processing (dangerous!) |
flat | boolean | Whether to flatten the output directory structure |
manifest | object | See the manifest |
suppressErrors | boolean | Disable error output |
errorOutput | string or function | Specify a different filename for errors, or provide a callback function to handle the error yourself. |
interface Config {
input: string | string[];
output: string;
pipeline: Pipeline;
concurrency: number;
clean?: boolean;
flat?: boolean;
manifest?: ManifestMappings;
suppressErrors?: boolean;
errorOutput: string | (item: Exception) => void | Promise<void>;
}
note
concurrency
is not actually required as it is populated by the initialisation script by default.
danger
When the clean
option is enabled, the path specified as the output folder will be irrecoverably
deleted with no additional checks in place. Use with care.
Manifest
- Table
- TypeScript
key | type | description |
---|---|---|
source | object | Per-image exported metadata keys |
format | object | Per-format exported metadata keys |
interface ManifestMappings {
source?: Record<string, string>;
format?: Record<string, string>;
}
The manifest property lets you select which metadata values you would like to include in the manifest file, essentially mapping metadata to new property names in the manifest.
There are two sub-properties, source
, which lets you include metadata that is relevant to the
source image, and format
which is exported separately for every format.
The key is used as the new name of the metadata value, while the value is the name of the
metadata key. The length selector ":
" may also be used to limit the size of the metadata value.
To learn more about the manifest, see this dedicated page.
Pipeline
type Pipeline = PipelineBranch[];
The pipeline is an array of pipeline branches. It is a recursive structure, as each pipeline branch may additionally define another pipeline that receives its output. It is best to imagine it like a tree, where each branch is a pipe that can split into more branches.
Pipeline branch
A pipeline is just a collection of ordered pipeline branches. A pipeline branch defines a single pipe that the image will be processed by, along with additional options and whether or not to save the exported formats.
It may also additionally define an ensuing pipeline that will receive the output of the pipe. Branches may only split, but not rejoin. This is to keep the pipeline schema as simple as possible.
Take a look at the above configuration example to try and get a feel for how to define a pipeline. To get a better understanding of how the pipeline works, you can learn more about the architecture.
- Table
- TypeScript
key | type | description |
---|---|---|
pipe | string or object or function | Per-image exported metadata keys (required) |
options | object | Per-format exported metadata keys |
save | string or true | Per-format exported metadata keys |
then | object | Per-format exported metadata keys |
interface PipelineBranch<O = any> {
pipe: string | { resolve: string; module?: string } | Pipe;
options?: O;
save?: PrimitiveValue;
then?: Pipeline;
}
The pipe property
This property tells IPP how to find the pipe. It can be:
- a string for a built-in pipe
- an object to resolve a pipe from an npm package
- a function (advanced usage)
When resolving using an object, you may define a resolve property which is the npm package name, and optionally a module property that lets you choose the module export (see ES Modules). See the example above for the primitive pipe.
note
It is not possible to define JavaScript functions in YAML or JSON configuration files. For this, you must use a JavaScript configuration file.
Saving images
To mark an image format for export, it must have a save
key. This key must be a string and is used
to determine the format's filename. The save key can contain template literals using square brackets
that are substituted with values from the metadata.
"[name]-[hash:8][ext]" → "image-89f25685.jpg"