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
Utility methods for path resolution, file matching, environment variables, ignore patterns, and file watching. These helpers provide essential functionality for configuring and extending Metalsmith behavior.
Resolve paths relative to the Metalsmith working directory.
/**
* Resolve paths relative to the metalsmith working directory
* @param paths - Path segments to resolve
* @returns Absolute path resolved from working directory
*/
path(...paths: string[]): string;Usage Examples:
import Metalsmith from "metalsmith";
const metalsmith = Metalsmith(__dirname);
// Resolve paths relative to working directory
const srcPath = metalsmith.path('src', 'content');
const buildPath = metalsmith.path('build');
const configPath = metalsmith.path('config', 'site.json');
console.log(srcPath); // "/absolute/path/to/project/src/content"
console.log(buildPath); // "/absolute/path/to/project/build"
console.log(configPath); // "/absolute/path/to/project/config/site.json"
// Use in plugins
function customPlugin(files, metalsmith, done) {
const dataPath = metalsmith.path('data', 'api.json');
const templatePath = metalsmith.path('templates');
// Use resolved paths for file operations
done();
}Match file paths against glob patterns using micromatch.
/**
* Match filepaths against glob patterns
* @param patterns - Glob pattern(s) to match against
* @param input - Array of file paths to test (defaults to current files)
* @param options - Micromatch options (excluding 'format')
* @returns Array of matching file paths
*/
match(
patterns: string | string[],
input?: string[],
options?: MatchOptions
): string[];
interface MatchOptions {
/** Include dotfiles in matches */
dot?: boolean;
/** Case-sensitive matching */
nocase?: boolean;
/** Additional micromatch options */
[key: string]: any;
}Usage Examples:
const files = await metalsmith.read();
const filePaths = Object.keys(files);
// Match all markdown files
const markdownFiles = metalsmith.match('**/*.md', filePaths);
// Match multiple patterns
const contentFiles = metalsmith.match([
'**/*.md',
'**/*.html',
'**/*.txt'
], filePaths);
// Match with options
const draftFiles = metalsmith.match('**/*draft*', filePaths, {
nocase: true // Case-insensitive
});
// Use in plugins
function selectivePlugin(files, metalsmith, done) {
const targetFiles = metalsmith.match('posts/*.md');
targetFiles.forEach(filepath => {
// Process only matching files
const file = files[filepath];
file.processed = true;
});
done();
}
// Default input uses current files
metalsmith.use((files, metalsmith, done) => {
// Automatically matches against Object.keys(files)
const jsFiles = metalsmith.match('**/*.js');
done();
});Get and set Metalsmith environment variables (case-insensitive).
/**
* Get single environment variable
* @param name - Variable name (case-insensitive)
* @returns Variable value or undefined
*/
env(name: string): string | number | boolean | null;
/**
* Get all environment variables
* @returns Object with all environment variables
*/
env(): { [key: string]: string | number | boolean | null };
/**
* Set single environment variable
* @param name - Variable name (case-insensitive)
* @param value - Variable value (primitives only)
* @returns Metalsmith instance for chaining
*/
env(name: string, value: string | number | boolean | null): Metalsmith;
/**
* Set multiple environment variables
* @param env - Object with variable names and values
* @returns Metalsmith instance for chaining
*/
env(env: { [key: string]: string | number | boolean | null }): Metalsmith;Usage Examples:
// Set environment variables
metalsmith
.env('NODE_ENV', 'production')
.env('DEBUG', '@metalsmith/*')
.env('SITE_URL', 'https://example.com');
// Set multiple at once
metalsmith.env({
NODE_ENV: process.env.NODE_ENV,
DEBUG: false,
BUILD_TIME: Date.now()
});
// Get environment variables
const nodeEnv = metalsmith.env('NODE_ENV');
const isDebug = metalsmith.env('DEBUG');
const allEnv = metalsmith.env();
console.log('Environment:', nodeEnv);
console.log('Debug mode:', isDebug);
console.log('All vars:', allEnv);
// Use in plugins
function environmentAwarePlugin(files, metalsmith, done) {
const isDev = metalsmith.env('NODE_ENV') === 'development';
const debugEnabled = metalsmith.env('DEBUG');
if (isDev) {
// Development-specific processing
}
if (debugEnabled) {
console.log('Processing files in debug mode');
}
done();
}
// Environment variables are case-insensitive
metalsmith.env('debug', true);
console.log(metalsmith.env('DEBUG')); // trueConfigure which files and directories to ignore during processing.
/**
* Add files/paths to ignore list
* @param files - File patterns, paths, or filter functions to ignore
* @returns Metalsmith instance for chaining
*/
ignore(files: string | string[] | IgnoreFunction | IgnoreFunction[]): Metalsmith;
/**
* Get current ignore list
* @returns Array of ignore patterns and functions
*/
ignore(): (string | IgnoreFunction)[];
type IgnoreFunction = (filepath: string, stats: import('fs').Stats) => boolean;Usage Examples:
import { Stats } from 'fs';
// Ignore specific files
metalsmith.ignore('README.md');
// Ignore patterns
metalsmith.ignore([
'**/.DS_Store',
'**/Thumbs.db',
'**/*.tmp',
'drafts/**'
]);
// Ignore using functions
metalsmith.ignore((filepath, stats) => {
// Ignore hidden files
return filepath.startsWith('.');
});
metalsmith.ignore((filepath, stats) => {
// Ignore large files (>1MB)
return stats.size > 1024 * 1024;
});
// Multiple ignore rules
metalsmith
.ignore('node_modules/**')
.ignore('*.log')
.ignore((filepath, stats) => {
// Ignore directories named 'temp'
return stats.isDirectory() && filepath.endsWith('/temp');
});
// Get current ignore list
const ignoreList = metalsmith.ignore();
console.log('Ignoring:', ignoreList);Configure file watching for automatic rebuilds (experimental feature).
/**
* Get current watch configuration
* @returns Current watch settings or false if disabled
*/
watch(): false | WatchOptions;
/**
* Enable/disable file watching
* @param enabled - True to watch source directory, false to disable
* @returns Metalsmith instance for chaining
*/
watch(enabled: boolean): Metalsmith;
/**
* Watch specific paths
* @param paths - Directory paths or glob patterns to watch
* @returns Metalsmith instance for chaining
*/
watch(paths: string | string[]): Metalsmith;
/**
* Configure watch options
* @param options - Chokidar watch options
* @returns Metalsmith instance for chaining
*/
watch(options: WatchOptions): Metalsmith;
interface WatchOptions {
/** Paths to watch (default: source directory) */
paths?: string[];
/** Wait for write operations to complete before triggering */
awaitWriteFinish?: boolean;
/** Ignore initial add events */
ignoreInitial?: boolean;
/** Use polling instead of native events */
usePolling?: boolean;
/** Polling interval in milliseconds */
interval?: number;
/** Additional chokidar options */
[key: string]: any;
}Usage Examples:
// Enable watching (experimental)
metalsmith
.clean(false) // Use partial rebuilds for better performance
.watch(true) // Watch source directory
.build((error, files) => {
if (error) {
console.error('Build error:', error);
metalsmith.watch(false); // Stop watching on error
return;
}
console.log(`Rebuilt ${Object.keys(files).length} files`);
});
// Watch specific directories
metalsmith.watch(['src', 'templates', 'data']);
// Advanced watch configuration
metalsmith.watch({
paths: ['src/**/*.md', 'templates/**/*.hbs'],
awaitWriteFinish: {
stabilityThreshold: 2000,
pollInterval: 100
},
ignoreInitial: true
});
// Get watch status
const watchConfig = metalsmith.watch();
if (watchConfig) {
console.log('Watching:', watchConfig.paths);
} else {
console.log('Watch mode disabled');
}
// Stop watching
metalsmith.watch(false);Common patterns for using utilities in plugins and workflows.
// Plugin using multiple utilities
function utilityPlugin(options = {}) {
return function plugin(files, metalsmith, done) {
const debug = metalsmith.env('DEBUG');
const pattern = options.pattern || '**/*.md';
// Use path utility for file operations
const dataPath = metalsmith.path('data', 'config.json');
// Match files using pattern
const targetFiles = metalsmith.match(pattern);
if (debug) {
console.log(`Processing ${targetFiles.length} files matching ${pattern}`);
}
targetFiles.forEach(filepath => {
const file = files[filepath];
// Process file...
});
done();
};
}
// Environment-based configuration
const isProduction = metalsmith.env('NODE_ENV') === 'production';
const debugEnabled = metalsmith.env('DEBUG');
metalsmith
.clean(isProduction) // Only clean in production
.concurrency(isProduction ? 50 : 10) // Higher concurrency in production
.ignore(isProduction ? [] : ['drafts/**']) // Include drafts in development
.env('MINIFY', isProduction); // Set flags for plugins
// Dynamic ignore patterns
metalsmith.ignore((filepath, stats) => {
const isDraft = files[filepath]?.draft === true;
const isProduction = metalsmith.env('NODE_ENV') === 'production';
// Ignore drafts in production
return isProduction && isDraft;
});