Comprehensive plugin options validation system using Joi schemas with async support, detailed error reporting, and Gatsby-specific extensions.
Validates plugin options against a Joi schema with comprehensive error handling and warning support.
/**
* Validates plugin options against a Joi schema
* @param pluginSchema - Joi object schema for validation
* @param pluginOptions - Plugin options to validate
* @param options - Validation configuration options
* @returns Promise with validated value and warnings
*/
function validateOptionsSchema(
pluginSchema: ObjectSchema,
pluginOptions: IPluginInfoOptions,
options: {
validateExternalRules?: boolean;
returnWarnings?: boolean;
} = {
validateExternalRules: true,
returnWarnings: true
}
): Promise<{
value: IPluginInfoOptions;
warning: {
message: string;
details: Array<{
message: string;
path: Array<string>;
type: string;
context: Array<Record<string, unknown>>;
}>;
};
}>;Usage Examples:
import { validateOptionsSchema, Joi } from "gatsby-plugin-utils";
// Define schema
const pluginSchema = Joi.object({
apiKey: Joi.string().required().description("API key for service"),
timeout: Joi.number().min(1000).default(5000).description("Request timeout in ms"),
retries: Joi.number().min(0).max(5).default(3).description("Number of retry attempts"),
enableCache: Joi.boolean().default(true).description("Enable response caching"),
endpoints: Joi.object({
graphql: Joi.string().uri().required(),
rest: Joi.string().uri().optional()
}).required()
});
// Validate options
async function validateMyPluginOptions(pluginOptions) {
try {
const result = await validateOptionsSchema(pluginSchema, pluginOptions, {
validateExternalRules: true,
returnWarnings: true
});
console.log("Validated options:", result.value);
if (result.warning?.details?.length > 0) {
console.warn("Validation warnings:", result.warning.details);
}
return result.value;
} catch (error) {
console.error("Validation failed:", error.details);
throw error;
}
}
// Example plugin options
const pluginOptions = {
apiKey: "sk-1234567890",
timeout: 3000,
endpoints: {
graphql: "https://api.example.com/graphql"
}
};
const validatedOptions = await validateMyPluginOptions(pluginOptions);Utility for testing plugin options schemas in test environments with comprehensive result reporting.
/**
* Tests plugin options schema for validation behavior
* @param pluginSchemaFunction - Function that returns Joi schema
* @param pluginOptions - Options to test against schema
* @returns Promise with detailed test results
*/
function testPluginOptionsSchema(
pluginSchemaFunction: (args: { Joi: PluginOptionsSchemaJoi }) => ObjectSchema,
pluginOptions: IPluginInfoOptions
): Promise<{
errors: Array<string>;
warnings: Array<string>;
isValid: boolean;
hasWarnings: boolean;
}>;Usage Examples:
import { testPluginOptionsSchema } from "gatsby-plugin-utils";
// Test schema function
const pluginSchema = ({ Joi }) =>
Joi.object({
name: Joi.string().required(),
version: Joi.string().pattern(/^\d+\.\d+\.\d+$/),
features: Joi.array().items(Joi.string()).default([])
});
// Test cases
describe("Plugin Options Schema", () => {
it("should validate correct options", async () => {
const { isValid, errors, warnings } = await testPluginOptionsSchema(
pluginSchema,
{
name: "my-plugin",
version: "1.0.0",
features: ["feature-a", "feature-b"]
}
);
expect(isValid).toBe(true);
expect(errors).toHaveLength(0);
});
it("should fail with missing required field", async () => {
const { isValid, errors } = await testPluginOptionsSchema(
pluginSchema,
{
version: "1.0.0"
}
);
expect(isValid).toBe(false);
expect(errors).toContain('"name" is required');
});
it("should warn about unknown fields", async () => {
const { isValid, hasWarnings, warnings } = await testPluginOptionsSchema(
pluginSchema,
{
name: "my-plugin",
version: "1.0.0",
unknownField: "value"
}
);
expect(isValid).toBe(true);
expect(hasWarnings).toBe(true);
expect(warnings).toContain('"unknownField" is not allowed');
});
});interface ValidationOptions {
/**
* Whether to validate external rules (default: true)
*/
validateExternalRules?: boolean;
/**
* Whether to return warnings alongside validation results (default: true)
*/
returnWarnings?: boolean;
}The validation system provides detailed error information for debugging:
try {
await validateOptionsSchema(schema, options);
} catch (validationError) {
// validationError.details contains array of specific validation failures
validationError.details.forEach(detail => {
console.log(`Field: ${detail.path.join('.')}`);
console.log(`Error: ${detail.message}`);
console.log(`Type: ${detail.type}`);
});
}The validation system includes Gatsby-specific Joi extensions:
// Extended Joi with subPlugins validation
const Joi = require("gatsby-plugin-utils").Joi;
const schemaWithSubPlugins = Joi.object({
plugins: Joi.subPlugins().description("Array of sub-plugins")
});
// Validates arrays of plugin references:
// ["plugin-name"] or [{ resolve: "plugin-name", options: {...} }]// gatsby-node.js
const { validateOptionsSchema } = require("gatsby-plugin-utils");
exports.pluginOptionsSchema = ({ Joi }) => {
return Joi.object({
apiToken: Joi.string().required().description("API authentication token"),
apiUrl: Joi.string().uri().default("https://api.example.com"),
cacheTimeout: Joi.number().min(0).default(300000)
});
};
exports.onPreInit = async ({ reporter }, pluginOptions) => {
try {
const schema = exports.pluginOptionsSchema({ Joi: require("gatsby-plugin-utils").Joi });
await validateOptionsSchema(schema, pluginOptions);
reporter.info("Plugin options validated successfully");
} catch (error) {
reporter.panic("Invalid plugin options", error);
}
};