Gatsby plugin lifecycle hooks and development server integration for seamless image processing within Gatsby's build pipeline.
Gatsby plugin lifecycle functions that integrate image processing with Gatsby's build process.
/**
* Sets up lazy image processing in development server
* @param options - Gatsby lifecycle options
* @param options.app - Express application instance
* @param options.cache - Gatsby cache instance
* @param options.reporter - Gatsby reporter instance
* @returns Promise that resolves when setup is complete
*/
function onCreateDevServer(options: {
app: Express.Application;
cache: GatsbyCache;
reporter: Reporter;
}): Promise<void>;
/**
* Processes remaining image jobs after bootstrap
* @param options - Gatsby lifecycle options
* @param options.reporter - Gatsby reporter instance
* @param options.cache - Gatsby cache instance
* @param options.store - Gatsby store instance
* @returns Promise that resolves when processing is complete
*/
function onPostBootstrap(options: {
reporter: Reporter;
cache: GatsbyCache;
store: GatsbyStore;
}): Promise<void>;
/**
* Initializes plugin in worker processes
* @param options - Gatsby lifecycle options
* @param options.actions - Gatsby actions object
* @param pluginOptions - Plugin configuration options
* @returns Promise that resolves when initialization is complete
*/
function onPluginInit(
options: { actions: Actions },
pluginOptions: ISharpPluginOptions
): Promise<void>;
/**
* Sets up plugin before Gatsby bootstrap
* @param options - Gatsby lifecycle options
* @param options.actions - Gatsby actions object
* @param options.emitter - Event emitter instance
* @param options.cache - Gatsby cache instance
* @param pluginOptions - Plugin configuration options
* @returns Promise that resolves when setup is complete
*/
function onPreBootstrap(
options: {
actions: Actions;
emitter: EventEmitter;
cache: GatsbyCache;
},
pluginOptions: ISharpPluginOptions
): Promise<void>;Usage in gatsby-node.js:
// These hooks are automatically called by Gatsby
// This is how the plugin integrates with Gatsby's lifecycle
exports.onPreBootstrap = async ({ actions, emitter, cache }, pluginOptions) => {
// Plugin initialization
console.log("Initializing gatsby-plugin-sharp...");
};
exports.onPluginInit = async ({ actions }, pluginOptions) => {
// Worker initialization
console.log("Initializing plugin in worker...");
};
exports.onCreateDevServer = async ({ app, cache, reporter }) => {
// Development server setup
console.log("Setting up lazy image processing...");
};
exports.onPostBootstrap = async ({ reporter, cache, store }) => {
// Process remaining jobs
console.log("Processing remaining image jobs...");
};Joi validation schema for plugin configuration options.
/**
* Defines Joi validation schema for plugin options
* @param options - Joi schema builder options
* @param options.Joi - Joi validation library instance
* @returns Joi schema object for plugin options validation
*/
function pluginOptionsSchema({ Joi }: { Joi: any }): JoiSchema;The schema validates the following options:
const optionsSchema = Joi.object({
base64Width: Joi.number()
.default(20)
.description("The width of the generated base64 preview image"),
forceBase64Format: Joi.any()
.valid("png", "jpg", "webp")
.description("Force a different format for the generated base64 image"),
useMozJpeg: Joi.boolean()
.description("Use the mozJpeg library for encoding"),
stripMetadata: Joi.boolean().default(true),
defaultQuality: Joi.number().default(50),
failOnError: Joi.boolean().default(true), // Deprecated
failOn: Joi.any()
.valid("none", "truncated", "error", "warning")
.default("warning")
.description("Level of sensitivity to invalid images"),
defaults: Joi.object({
formats: Joi.array().items(
Joi.string().valid("auto", "png", "jpg", "webp", "avif")
),
placeholder: Joi.string().valid(
"tracedSVG", "dominantColor", "blurred", "none"
),
quality: Joi.number(),
breakpoints: Joi.array().items(Joi.number()),
backgroundColor: Joi.string(),
transformOptions: Joi.object(),
tracedSVGOptions: Joi.object(),
blurredOptions: Joi.object(),
jpgOptions: Joi.object(),
pngOptions: Joi.object(),
webpOptions: Joi.object(),
avifOptions: Joi.object(),
}).description("Default options used by gatsby-plugin-image")
});In development mode, the plugin sets up lazy image processing to improve build performance:
// Middleware for handling lazy image requests
app.use(async (req, res, next) => {
const decodedURI = decodeURIComponent(req.path);
const pathOnDisk = path.resolve(path.join("./public/", decodedURI));
// Check if this is a lazy image request
const jobContentDigest = await cache.get(decodedURI);
const cacheResult = jobContentDigest ? await cache.get(jobContentDigest) : null;
if (cacheResult) {
// Process the image on-demand
await processLazyImage(cacheResult, pathOnDisk, reporter);
return res.sendFile(pathOnDisk);
}
next();
});The plugin integrates with Gatsby's job system for efficient image processing:
/**
* Internal job creation function (unstable API)
* @param job - Job configuration object
* @param options - Job options including reporter
* @returns Promise that resolves when job is created
*/
function _unstable_createJob(job: JobConfig, options: { reporter: Reporter }): Promise<void>;
/**
* Internal function to check if lazy job processing is enabled
* @returns Boolean indicating if lazy jobs are enabled
*/
function _lazyJobsEnabled(): boolean;
interface JobConfig {
name: string; // Job name identifier
inputPaths: string[]; // Input file paths
outputDir: string; // Output directory
args: {
isLazy: boolean; // Whether job should be processed lazily
operations: Array<{ // Processing operations
outputPath: string;
args: ITransformArgs;
}>;
pluginOptions: ISharpPluginOptions;
};
}The plugin uses Gatsby's worker system for concurrent image processing:
/**
* Worker function for processing image transformation jobs
* @param options - Worker job options
* @param options.inputPaths - Array of input file paths
* @param options.outputDir - Output directory for processed images
* @param options.args - Processing arguments and operations
* @returns Promise that resolves when processing is complete
*/
function IMAGE_PROCESSING(options: {
inputPaths: Array<{ path: string }>;
outputDir: string;
args: {
isLazy: boolean;
operations: Array<{
outputPath: string;
args: ITransformArgs;
}>;
pluginOptions: ISharpPluginOptions;
};
}): Promise<void>;
/**
* Job name identifier for image processing jobs
*/
const IMAGE_PROCESSING_JOB_NAME = "IMAGE_PROCESSING";// Worker queue configuration
const queue = require("async/queue");
const { cpuCoreCount } = require("gatsby-core-utils/cpu-core-count");
const concurrency = process.env.GATSBY_WORKER_POOL_WORKER
? 1 // Single core in worker processes
: Math.max(1, cpuCoreCount() - 1); // Multi-core in main process// gatsby-node.js - Custom image processing resolver
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
File: {
customImageProcessing: {
type: "JSON",
async resolve(source, args, context) {
const { queueImageResizing } = require("gatsby-plugin-sharp");
return await queueImageResizing({
file: source,
args: {
width: args.width,
height: args.height,
quality: args.quality || 80
},
reporter: context.reporter
});
}
}
}
};
createResolvers(resolvers);
};// gatsby-node.js - Process images during build
exports.onCreateNode = async ({ node, actions, reporter, cache }) => {
const { createNode } = actions;
if (node.internal.type === "File" && node.extension === "jpg") {
const { generateImageData } = require("gatsby-plugin-sharp");
const imageData = await generateImageData({
file: node,
args: {
layout: "constrained",
width: 800,
formats: ["auto", "webp"]
},
pathPrefix: "/",
reporter,
cache
});
if (imageData) {
createNode({
...imageData,
id: `${node.id}-processed`,
parent: node.id,
internal: {
type: "ProcessedImage",
contentDigest: node.internal.contentDigest
}
});
}
}
};// Creating a custom plugin that extends gatsby-plugin-sharp
const { setActions } = require("gatsby-plugin-sharp");
exports.onPreBootstrap = ({ actions }) => {
// Initialize gatsby-plugin-sharp actions
setActions(actions);
};
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
File: {
watermarkedImage: {
type: "JSON",
async resolve(source, args, context) {
const { queueImageResizing } = require("gatsby-plugin-sharp");
// Custom watermarking logic
return await queueImageResizing({
file: source,
args: {
width: args.width,
quality: 85,
// Custom watermark processing would go here
},
reporter: context.reporter
});
}
}
}
};
createResolvers(resolvers);
};The plugin integrates with Gatsby's error reporting system:
const { reportError } = require("gatsby-plugin-sharp/src/report-error");
// Report processing errors
try {
await processImage(file);
} catch (error) {
reportError("Failed to process image", error, reporter);
}// Enable debug logging
const debug = require("debug")("gatsby:gatsby-plugin-sharp");
debug("Processing image:", file.absolutePath);
debug("Transform args:", args);// Monitor job completion
emitter.on("END_JOB_V2", action => {
if (action.plugin.name === "gatsby-plugin-sharp") {
console.log(`Completed image processing job: ${action.payload.jobContentDigest}`);
}
});// gatsby-config.js - Production optimized
module.exports = {
plugins: [
{
resolve: "gatsby-plugin-sharp",
options: {
defaultQuality: 85,
stripMetadata: true,
useMozJpeg: true,
failOn: "error",
defaults: {
formats: ["auto", "webp", "avif"],
placeholder: "blurred",
quality: 90
}
}
}
]
};// gatsby-config.js - Development optimized
module.exports = {
plugins: [
{
resolve: "gatsby-plugin-sharp",
options: {
defaultQuality: 60, // Lower quality for faster builds
stripMetadata: false, // Keep metadata for debugging
failOn: "warning", // More lenient error handling
defaults: {
formats: ["auto"], // Fewer formats for speed
placeholder: "dominantColor" // Faster placeholder generation
}
}
}
]
};