Wrapper of the Sharp image manipulation library for Gatsby plugins
57
Pending
Does it follow best practices?
Impact
57%
1.07xAverage score across 10 eval scenarios
Pending
The risk profile of this skill
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
}
}
}
]
};docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10