An extremely simple, pluggable static site generator for NodeJS
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Debugging system with namespaced loggers and configurable output for development and troubleshooting. Metalsmith uses the debug module with enhanced features for plugin development and build monitoring.
Create namespaced debug loggers for plugins and custom code.
/**
* Create a namespaced debug logger
* @param namespace - Debug namespace (typically plugin name)
* @returns Enhanced debugger with multiple log levels
*/
debug(namespace: string): Debugger;
interface Debugger {
/** Main debug function for general messages */
(message: string, ...args: any[]): void;
/** Info-level logging (namespace:info) */
info(message: string, ...args: any[]): void;
/** Warning-level logging (namespace:warn) */
warn(message: string, ...args: any[]): void;
/** Error-level logging (namespace:error) */
error(message: string, ...args: any[]): void;
}Usage Examples:
import Metalsmith from "metalsmith";
const metalsmith = Metalsmith(__dirname);
// Create debug logger for a plugin
function myPlugin(files, metalsmith, done) {
const debug = metalsmith.debug('metalsmith-myplugin');
debug('Starting plugin execution');
debug('Processing %d files', Object.keys(files).length);
Object.keys(files).forEach(filepath => {
debug('Processing file: %s', filepath);
try {
// Process file...
debug.info('Successfully processed: %s', filepath);
} catch (error) {
debug.error('Failed to process %s: %s', filepath, error.message);
}
});
debug('Plugin execution completed');
done();
}
// Use different log levels
function detailedPlugin(files, metalsmith, done) {
const debug = metalsmith.debug('detailed-plugin');
debug('Plugin started'); // General debug
debug.info('Configuration loaded'); // Info level
debug.warn('Deprecated option used'); // Warning level
debug.error('Critical error occurred'); // Error level
done();
}Control debug output through environment variables and Metalsmith settings.
/**
* Debug configuration properties (available on metalsmith.debug)
*/
interface DebugConfig {
/** Enable debug output for specific namespaces */
enable(namespaces: string): void;
/** Disable debug output */
disable(): void;
/** Set custom log handler function */
handle: (...args: any[]) => void;
/** Enable/disable color output */
colors: boolean;
/** Check if debug is currently enabled */
enabled: boolean;
}Environment Variable Control:
// Enable debug output via environment
metalsmith.env('DEBUG', '@metalsmith/*'); // All metalsmith plugins
metalsmith.env('DEBUG', 'metalsmith-markdown'); // Specific plugin
metalsmith.env('DEBUG', '*'); // All debug output
// Enable in code
metalsmith.debug.enable('@metalsmith/*');
// Disable debug output
metalsmith.debug.disable();
// Check if debug is enabled
if (metalsmith.debug.enabled) {
console.log('Debug mode is active');
}Color Configuration:
// Enable colors (default for terminals)
metalsmith.debug.colors = true;
// Disable colors (useful for log files)
metalsmith.debug.colors = false;Configure custom log handlers for debug output.
/**
* Set custom debug log handler
* @param handler - Function to handle debug output
*/
debug.handle = (message: string) => void;Usage Examples:
import fs from 'fs';
// Log to file
const logStream = fs.createWriteStream('debug.log', { flags: 'a' });
metalsmith.debug.handle = (message) => {
logStream.write(message + '\n');
};
// Custom formatted logging
metalsmith.debug.handle = (message) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${message}`);
};
// Multiple handlers
const originalHandler = metalsmith.debug.handle;
metalsmith.debug.handle = (message) => {
originalHandler(message); // Console output
logStream.write(`${new Date().toISOString()}: ${message}\n`); // File output
};Metalsmith includes enhanced debug features beyond standard debug module.
Buffer Formatter:
// Debug includes a %b formatter for buffers
function plugin(files, metalsmith, done) {
const debug = metalsmith.debug('my-plugin');
Object.keys(files).forEach(filepath => {
const file = files[filepath];
// %b formatter shows buffer content preview
debug('File contents: %b', file.contents);
// Output: "File contents: <Buffer contents preview...>"
});
done();
}Performance Monitoring:
function performancePlugin(files, metalsmith, done) {
const debug = metalsmith.debug('performance');
const startTime = Date.now();
// Process files...
const duration = Date.now() - startTime;
debug('Processing completed in %dms', duration);
debug.info('Processed %d files', Object.keys(files).length);
done();
}Using debug output to monitor the build process.
// Enable comprehensive debugging
metalsmith
.env('DEBUG', '*')
.env('DEBUG_LOG', 'build.log') // Optional: log to file
.build((error, files) => {
if (error) {
console.error('Build failed:', error);
return;
}
console.log('Build completed successfully');
});Best practices for debugging in plugins.
function robustPlugin(options = {}) {
return function plugin(files, metalsmith, done) {
const debug = metalsmith.debug('robust-plugin');
// Log plugin start with configuration
debug('Plugin starting with options: %o', options);
const startTime = process.hrtime();
const fileCount = Object.keys(files).length;
debug('Processing %d files', fileCount);
try {
let processed = 0;
Object.keys(files).forEach(filepath => {
debug('Processing file %d/%d: %s', ++processed, fileCount, filepath);
const file = files[filepath];
// Detailed file information
debug('File size: %d bytes', file.contents.length);
debug('File metadata: %o', {
title: file.title,
date: file.date,
tags: file.tags
});
// Process file with error handling
try {
// File processing logic...
debug.info('Successfully processed: %s', filepath);
} catch (fileError) {
debug.error('Error processing %s: %s', filepath, fileError.message);
throw fileError; // Re-throw to stop build
}
});
const [seconds, nanoseconds] = process.hrtime(startTime);
const duration = (seconds * 1000) + (nanoseconds / 1000000);
debug('Plugin completed in %dms', duration.toFixed(2));
debug.info('Successfully processed %d files', processed);
done();
} catch (error) {
debug.error('Plugin failed: %s', error.message);
debug.error('Stack trace: %s', error.stack);
done(error);
}
};
}Configure debugging based on environment settings.
// Development environment with verbose debugging
if (process.env.NODE_ENV === 'development') {
metalsmith
.env('DEBUG', '@metalsmith/*,my-plugin*')
.env('DEBUG_LOG', 'logs/debug.log');
metalsmith.debug.colors = true;
}
// Production environment with error-only logging
if (process.env.NODE_ENV === 'production') {
metalsmith.env('DEBUG', false);
// Custom error-only handler
metalsmith.debug.handle = (message) => {
if (message.includes(':error')) {
console.error(message);
}
};
}
// Conditional debugging in plugins
function conditionalPlugin(files, metalsmith, done) {
const debug = metalsmith.debug('conditional-plugin');
const verbose = metalsmith.env('VERBOSE');
if (verbose) {
debug('Verbose mode enabled');
debug('File list: %o', Object.keys(files));
}
// Process files...
done();
}Common debugging scenarios for file processing problems.
function diagnosticPlugin(files, metalsmith, done) {
const debug = metalsmith.debug('diagnostics');
debug('=== DIAGNOSTIC INFORMATION ===');
debug('Working directory: %s', metalsmith.directory());
debug('Source directory: %s', metalsmith.source());
debug('Destination directory: %s', metalsmith.destination());
debug('Global metadata: %o', metalsmith.metadata());
debug('Environment: %o', metalsmith.env());
debug('=== FILE ANALYSIS ===');
debug('Total files: %d', Object.keys(files).length);
Object.keys(files).forEach(filepath => {
const file = files[filepath];
debug('File: %s', filepath);
debug(' Size: %d bytes', file.contents.length);
debug(' Mode: %s', file.mode);
debug(' Stats: %o', file.stats);
debug(' Metadata keys: %o', Object.keys(file).filter(k => !['contents', 'stats', 'mode'].includes(k)));
// Check for front-matter parsing issues
if (file.contents.toString().startsWith('---')) {
debug.warn(' Potential front-matter parsing issue detected');
}
});
done();
}