🎯 Recommended Samples
Balanced sample collections from various categories for you to explore
Gulp Task Automation
Gulp task runner examples including build automation, file processing, and development workflows
💻 Gulp Basic Tasks javascript
🟢 simple
⭐⭐⭐⭐
Essential Gulp tasks for modern web development including file processing, watching, and building
⏱️ 45 min
🏷️ rollup, plugins, advanced
Prerequisites:
Rollup basics, JavaScript AST, Plugin architecture
// Gulp Basic Tasks Setup
// Modern Gulp 4.x configuration for web development workflows
// ===== package.json =====
{
"name": "my-gulp-project",
"version": "1.0.0",
"description": "Modern web application using Gulp for task automation",
"main": "index.js",
"scripts": {
"dev": "gulp dev",
"build": "gulp build",
"watch": "gulp watch",
"clean": "gulp clean",
"serve": "gulp serve",
"test": "gulp test",
"lint": "gulp lint",
"deploy": "gulp deploy"
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-cli": "^2.3.0",
"gulp-sass": "^5.1.0",
"gulp-postcss": "^9.0.1",
"autoprefixer": "^10.4.0",
"cssnano": "^5.1.0",
"gulp-terser": "^2.1.0",
"gulp-babel": "^8.0.0",
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/preset-react": "^7.18.0",
"gulp-imagemin": "^8.0.0",
"gulp-rename": "^2.0.0",
"gulp-uglify": "^3.0.2",
"gulp-clean-css": "^4.3.0",
"gulp-concat": "^2.6.1",
"gulp-sourcemaps": "^3.0.0",
"gulp-eslint": "^6.0.0",
"gulp-stylelint": "^13.0.0",
"gulp-htmlmin": "^5.0.1",
"gulp-file-include": "^2.3.0",
"gulp-replace": "^1.1.4",
"gulp-size": "^4.0.0",
"gulp-brotli": "^2.0.2",
"gulp-zip": "^5.1.0",
"gulp-plumber": "^1.2.1",
"gulp-if": "^3.0.0",
"gulp-notify": "^4.0.0",
"browser-sync": "^2.27.0",
"del": "^7.0.0",
"through2": "^4.0.2",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"node-sass": "^8.0.0",
"tailwindcss": "^3.2.0",
"postcss": "^8.4.0",
"webpack-stream": "^7.0.0",
"dotenv": "^16.0.0",
"cross-env": "^7.0.3"
}
}
// ===== gulpfile.js =====
const { src, dest, watch, series, parallel, task } = require('gulp');
const gulp = require('gulp');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const sourcemaps = require('gulp-sourcemaps');
const rename = require('gulp-rename');
const replace = require('gulp-replace');
const size = require('gulp-size');
const del = require('del');
// Load environment variables
require('dotenv').config();
// Configuration
const config = {
src: {
html: 'src/**/*.html',
css: 'src/scss/**/*.scss',
js: 'src/js/**/*.js',
images: 'src/images/**/*',
fonts: 'src/fonts/**/*',
assets: 'src/assets/**/*'
},
build: {
html: 'dist',
css: 'dist/css',
js: 'dist/js',
images: 'dist/images',
fonts: 'dist/fonts',
assets: 'dist'
},
isProduction: process.env.NODE_ENV === 'production',
isDevelopment: process.env.NODE_ENV === 'development'
};
// Error handler
function errorHandler(err) {
notify.onError({
title: 'Gulp Error',
message: '<%= error.message %>'
})(err);
this.emit('end');
}
// Clean build directory
function clean() {
return del(['dist/**/*', '!dist/.gitkeep']);
}
// HTML tasks
const htmlmin = require('gulp-htmlmin');
const fileInclude = require('gulp-file-include');
function html() {
return src(config.src.html)
.pipe(plumber({ errorHandler }))
.pipe(fileInclude({
prefix: '@@',
basepath: 'src/'
}))
.pipe(gulpIf(config.isProduction, htmlmin({
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(replace('%%VERSION%%', process.env.npm_package_version || '1.0.0'))
.pipe(gulpIf(config.isProduction, size({ title: 'HTML:', showFiles: true })))
.pipe(dest(config.build.html));
}
// CSS/SCSS tasks
const sass = require('gulp-sass')(require('node-sass'));
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const cleanCSS = require('gulp-clean-css');
function styles() {
const processors = [
autoprefixer(),
gulpIf(config.isProduction, cssnano())
];
return src(config.src.css)
.pipe(plumber({ errorHandler }))
.pipe(gulpIf(config.isDevelopment, sourcemaps.init()))
.pipe(sass({
outputStyle: config.isProduction ? 'compressed' : 'expanded',
includePaths: ['node_modules']
}).on('error', sass.logError))
.pipe(postcss(processors))
.pipe(gulpIf(config.isProduction, cleanCSS()))
.pipe(rename({ suffix: '.min' }))
.pipe(gulpIf(config.isDevelopment, sourcemaps.write('.')))
.pipe(gulpIf(config.isProduction, size({ title: 'CSS:', showFiles: true })))
.pipe(dest(config.build.css));
}
// JavaScript tasks
const babel = require('gulp-babel');
const terser = require('gulp-terser');
const concat = require('gulp-concat');
function scripts() {
return src(config.src.js)
.pipe(plumber({ errorHandler }))
.pipe(gulpIf(config.isDevelopment, sourcemaps.init()))
.pipe(babel({
presets: [
['@babel/preset-env', {
targets: {
browsers: ['last 2 versions', 'not dead']
},
modules: false
}]
]
}))
.pipe(gulpIf(config.isProduction, concat('bundle.min.js')))
.pipe(gulpIf(config.isProduction, terser({
compress: {
drop_console: true,
drop_debugger: true
}
})))
.pipe(gulpIf(config.isDevelopment, sourcemaps.write('.')))
.pipe(gulpIf(config.isProduction, size({ title: 'JavaScript:', showFiles: true })))
.pipe(dest(config.build.js));
}
// Vendor JavaScript (libraries from node_modules)
function vendorScripts() {
const vendorFiles = [
'node_modules/jquery/dist/jquery.min.js',
'node_modules/bootstrap/dist/js/bootstrap.bundle.min.js'
];
return src(vendorFiles)
.pipe(concat('vendor.min.js'))
.pipe(gulpIf(config.isProduction, terser()))
.pipe(dest(config.build.js));
}
// Image optimization
const imagemin = require('gulp-imagemin');
function images() {
return src(config.src.images)
.pipe(plumber({ errorHandler }))
.pipe(imagemin([
imagemin.gifsicle({ interlaced: true }),
imagemin.mozjpeg({ quality: 80, progressive: true }),
imagemin.optipng({ optimizationLevel: 5 }),
imagemin.svgo({
plugins: [
{ removeViewBox: false },
{ collapseGroups: true }
]
})
]))
.pipe(gulpIf(config.isProduction, size({ title: 'Images:', showFiles: true })))
.pipe(dest(config.build.images));
}
// Font handling
function fonts() {
return src(config.src.fonts)
.pipe(plumber({ errorHandler }))
.pipe(dest(config.build.fonts));
}
// Other assets
function assets() {
return src(config.src.assets)
.pipe(plumber({ errorHandler }))
.pipe(dest(config.build.assets));
}
// Code quality tasks
const eslint = require('gulp-eslint');
const stylelint = require('gulp-stylelint');
function lintJS() {
return src(config.src.js)
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError());
}
function lintCSS() {
return src(config.src.css)
.pipe(stylelint({
reporters: [
{ formatter: 'string', console: true }
]
}));
}
// Development server
const browserSync = require('browser-sync').create();
function serve() {
browserSync.init({
server: 'dist',
port: 3000,
open: true,
notify: false,
ghostMode: false,
files: [
'dist/css/**/*.css',
'dist/js/**/*.js',
'dist/**/*.html'
]
});
watch(config.src.html, html);
watch(config.src.css, styles);
watch(config.src.js, scripts);
watch(config.src.images, images);
watch(config.src.fonts, fonts);
watch(config.src.assets, assets);
}
// Build tasks
const build = series(clean, parallel(html, styles, scripts, vendorScripts, images, fonts, assets));
// Development task
const dev = series(clean, parallel(html, styles, scripts, vendorScripts, images, fonts, assets), serve);
// Production build
function production() {
process.env.NODE_ENV = 'production';
return build();
}
// Watch task
function watchFiles() {
watch(config.src.html, html);
watch(config.src.css, styles);
watch(config.src.js, scripts);
watch(config.src.images, images);
watch(config.src.fonts, fonts);
watch(config.src.assets, assets);
}
// Export tasks
exports.clean = clean;
exports.html = html;
exports.styles = styles;
exports.scripts = scripts;
exports.vendorScripts = vendorScripts;
exports.images = images;
exports.fonts = fonts;
exports.assets = assets;
exports.lint = parallel(lintJS, lintCSS);
exports.build = build;
exports.dev = dev;
exports.production = production;
exports.watch = watchFiles;
exports.serve = serve;
exports.default = dev;
// ===== gulpfile.config.js =====
module.exports = {
src: {
root: 'src',
html: 'src/**/*.html',
css: 'src/scss/**/*.scss',
js: 'src/js/**/*.js',
ts: 'src/ts/**/*.ts',
jsx: 'src/jsx/**/*.jsx',
tsx: 'src/tsx/**/*.tsx',
images: 'src/images/**/*',
fonts: 'src/fonts/**/*',
icons: 'src/icons/**/*',
data: 'src/data/**/*',
templates: 'src/templates/**/*'
},
build: {
root: 'dist',
html: 'dist',
css: 'dist/css',
js: 'dist/js',
vendor: 'dist/vendor',
images: 'dist/images',
fonts: 'dist/fonts',
icons: 'dist/icons',
data: 'dist/data',
templates: 'dist/templates'
},
temp: {
root: '.tmp',
styles: '.tmp/styles'
},
nodeModules: 'node_modules',
browsersync: {
server: {
baseDir: ['dist'],
index: 'index.html'
},
port: 3000,
open: true,
notify: true,
ghostMode: {
clicks: true,
scroll: true,
forms: true
}
},
plumber: {
errorHandler: function(err) {
notify.onError({
title: 'Gulp Error',
message: '<%= error.message %>',
sound: 'Beep'
})(err);
this.emit('end');
}
},
autoprefixer: {
overrideBrowserslist: ['last 2 versions', 'not dead'],
cascade: true
},
sass: {
outputStyle: 'expanded',
includePaths: ['node_modules']
},
esLint: {
configFile: '.eslintrc.json',
failAfterError: false
},
styleLint: {
configFile: '.stylelintrc.json',
failAfterError: false
},
imagemin: {
optimizationLevel: 5,
progressive: true,
interlaced: true,
multipass: true
},
htmlmin: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
minifyCSS: true,
minifyJS: true,
processConditionalComments: true
},
terser: {
compress: {
drop_console: true,
drop_debugger: true,
collapse_vars: true,
reduce_vars: true
},
mangle: true,
output: {
comments: false
}
},
cleanCSS: {
level: 2,
return Promise.resolve([file]);
}
return Promise.resolve(file);
}
async process(bundleGraph) {
const outputOptions = this.options;
// Store bundle graph for later use
this.bundleGraph = bundleGraph;
// Run original bundling
const result = await this.defaultBundler.bundle(bundleGraph, outputOptions);
// Run post-processing
await this.postProcess(result, bundleGraph);
return result;
}
async postProcess(bundleResult, bundleGraph) {
// Additional processing after bundling
console.log('Running post-processing...');
// Example: Generate statistics
const stats = await this.generateStats(bundleGraph);
this.emit('buildStats', stats);
}
async generateStats(bundleGraph) {
const bundles = bundleGraph.getBundles();
const stats = {
bundleCount: bundles.length,
totalSize: 0,
bundles: []
};
bundles.forEach(bundle => {
const size = this.getBundleSize(bundle);
stats.totalSize += size;
stats.bundles.push({
name: bundle.getEntryAssets()[0]?.filePath || 'unknown',
size,
type: bundle.type
});
});
return stats;
}
getBundleSize(bundle) {
let size = 0;
bundle.getEntryAssets().forEach(asset => {
size += asset.stats.size;
});
return size;
}
} catch (error) {
return originalBundler;
}
// Add catch-all to prevent fatal errors
} else {
console.warn('Bundler not found in context, skipping plugin');
}
// Ensure we always return the original bundler
return originalBundler;
} catch (error) {
return originalBundler;
} finally {
// Reset error if it was thrown
if (originalBundler) {
return originalBundler;
}
}
// Final fallback
return originalBundler || class MockBundler {
async bundle() {
console.warn('Mock bundler used');
}
};
}
module.exports = bundlerWrapper;
// ===== PLUGIN ENHANCEMENT =====
// parcel-plugin-enhancer.js
const { promises: fs } = require('fs');
class PluginEnhancer {
constructor() {
this.enhancements = new Map();
this.middlewares = [];
}
enhance(pluginName, enhancer) {
if (!this.enhancements.has(pluginName)) {
this.enhancements.set(pluginName, []);
}
this.enhancements.get(pluginName).push(enhancer);
}
addMiddleware(middleware) {
this.middlewares.push(middleware);
}
async applyEnhancements(plugin, pluginName) {
const enhancements = this.enhancements.get(pluginName) || [];
let enhancedPlugin = plugin;
for (const enhancer of enhancements) {
enhancedPlugin = await enhancer(enhancedPlugin);
}
// Apply middlewares
for (const middleware of this.middlewares) {
enhancedPlugin = await middleware(enhancedPlugin, pluginName);
}
return enhancedPlugin;
}
}
const enhancer = new PluginEnhancer();
// Add common enhancements
enhancer.enhance('@parcel/transformer-js', (transformer) => {
return {
...transformer,
transformWithEnhancements: async (asset) => {
const result = await transformer.transform(asset);
// Add enhancement features
if (result.code) {
// Add source mapping support
result.code += `\n// Enhanced by parcel-plugin-optimizer`;
}
return result;
}
};
});
// Add middleware
enhancer.addMiddleware(async (plugin, pluginName) => {
if (typeof plugin === 'function') {
return async (...args) => {
const result = await plugin(...args);
// Log plugin usage
console.log(`Using enhanced plugin: ${pluginName}`);
return result;
};
}
return plugin;
});
module.exports = enhancer;
// ===== REAL-TIME PLUGIN SYSTEM =====
// parcel-plugin-system.js
class ParcelPluginSystem {
constructor() {
this.plugins = new Map();
this.hooks = new Map();
this.config = new Map();
this.status = 'idle';
this.metrics = {
pluginsLoaded: 0,
hooksExecuted: 0,
errors: 0
};
}
// Plugin registration
registerPlugin(name, plugin) {
if (this.plugins.has(name)) {
throw new Error(`Plugin ${name} already registered`);
}
this.plugins.set(name, {
name,
plugin,
status: 'registered',
loadedAt: null,
error: null
});
this.emit('pluginRegistered', { name, plugin });
return this;
}
// Load all registered plugins
async loadPlugins() {
this.status = 'loading';
const loadPromises = Array.from(this.plugins.entries()).map(
([name, { plugin }]) => this.loadPlugin(name, plugin)
);
await Promise.allSettled(loadPromises);
this.status = 'loaded';
this.emit('pluginsLoaded', { count: this.metrics.pluginsLoaded });
}
async loadPlugin(name, plugin) {
const pluginInfo = this.plugins.get(name);
try {
// Initialize plugin if it has init method
if (typeof plugin.init === 'function') {
await plugin.init(this);
}
pluginInfo.status = 'loaded';
pluginInfo.loadedAt = Date.now();
this.metrics.pluginsLoaded++;
this.emit('pluginLoaded', { name });
} catch (error) {
pluginInfo.status = 'error';
pluginInfo.error = error;
this.metrics.errors++;
this.emit('pluginError', { name, error });
}
}
// Hook registration
registerHook(hookName, handler, options = {}) {
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, []);
}
this.hooks.get(hookName).push({
handler,
options,
id: Math.random().toString(36).substr(2, 9)
});
return this;
}
// Execute hooks
async executeHook(hookName, context = {}) {
const handlers = this.hooks.get(hookName) || [];
const results = [];
this.metrics.hooksExecuted++;
for (const { handler, options } of handlers) {
try {
const result = await handler(context, this);
if (options.stopPropagation && result === false) {
break;
}
results.push(result);
} catch (error) {
this.metrics.errors++;
this.emit('hookError', { hookName, error });
if (options.breakOnError) {
throw error;
}
}
}
return results;
}
// Configuration management
setConfig(key, value) {
this.config.set(key, value);
this.emit('configChanged', { key, value });
}
getConfig(key) {
return this.config.get(key);
}
// Event emission
emit(event, data) {
// Implement event emission
console.log(`[PluginSystem] ${event}:`, data);
}
// Plugin unloading
async unloadPlugin(name) {
const pluginInfo = this.plugins.get(name);
if (!pluginInfo) {
throw new Error(`Plugin ${name} not found`);
}
try {
// Call plugin cleanup if available
if (pluginInfo.plugin && typeof pluginInfo.plugin.cleanup === 'function') {
await pluginInfo.plugin.cleanup();
}
pluginInfo.status = 'unloaded';
this.emit('pluginUnloaded', { name });
} catch (error) {
pluginInfo.error = error;
this.emit('pluginUnloadError', { name, error });
}
}
// Get plugin status
getPluginStatus(name) {
const pluginInfo = this.plugins.get(name);
return pluginInfo ? pluginInfo.status : 'not_found';
}
// List all plugins
listPlugins() {
return Array.from(this.plugins.entries()).map(([name, info]) => ({
name,
status: info.status,
error: info.error,
loadedAt: info.loadedAt
}));
}
// Performance metrics
getMetrics() {
return {
...this.metrics,
status: this.status,
pluginCount: this.plugins.size,
hookCount: Array.from(this.hooks.values()).reduce((sum, hooks) => sum + hooks.length, 0)
};
}
// Cleanup
async cleanup() {
this.status = 'cleaning';
const cleanupPromises = Array.from(this.plugins.entries())
.filter(([name, info]) => info.status === 'loaded')
.map(([name]) => this.unloadPlugin(name));
await Promise.allSettled(cleanupPromises);
this.plugins.clear();
this.hooks.clear();
this.config.clear();
this.status = 'cleaned';
this.emit('systemCleanup');
}
}
// Example plugin system usage
const pluginSystem = new ParcelPluginSystem();
// Register a sample plugin
pluginSystem.registerPlugin('samplePlugin', {
init: async (system) => {
console.log('Sample plugin initialized');
system.setConfig('samplePlugin.enabled', true);
},
transform: async (asset) => {
// Simple transformation
if (asset.type === 'js') {
asset.code += `\n// Processed by sample plugin`;
}
return asset;
},
cleanup: async () => {
console.log('Sample plugin cleanup');
}
});
// Register hooks
pluginSystem.registerHook('beforeBuild', async (context, system) => {
console.log('Before build hook executed');
});
pluginSystem.registerHook('afterBuild', async (context, system) => {
console.log('After build hook executed');
console.log('Build metrics:', system.getMetrics());
});
module.exports = ParcelPluginSystem;
💻 Rollup Plugin Ecosystem javascript
🟡 intermediate
⭐⭐⭐⭐
Building plugins that integrate with the broader Rollup ecosystem and common patterns
⏱️ 40 min
🏷️ rollup, plugins, ecosystem
Prerequisites:
Rollup plugin development, JavaScript ecosystems, Build tools
// Rollup Plugin Ecosystem Integration
// Building plugins that work seamlessly with other Rollup plugins and tools
// ===== PLUGIN INTEROPERABILITY =====
// 1. Plugin Communication System
// plugin-communication.js
export class PluginCommunication {
constructor() {
this.channels = new Map();
this.globalState = new Map();
}
// Subscribe to a channel
subscribe(channel, callback) {
if (!this.channels.has(channel)) {
this.channels.set(channel, []);
}
this.channels.get(channel).push(callback);
}
// Publish to a channel
publish(channel, data) {
const subscribers = this.channels.get(channel) || [];
subscribers.forEach(callback => callback(data));
}
// Set global state
setState(key, value) {
this.globalState.set(key, value);
this.publish('stateChange', { key, value });
}
// Get global state
getState(key) {
return this.globalState.get(key);
}
}
// 2. Plugin Registry System
// plugin-registry.js
export class PluginRegistry {
constructor() {
this.plugins = new Map();
this.hooks = new Map();
this.communication = new PluginCommunication();
}
// Register a plugin
register(plugin) {
if (!plugin.name) {
throw new Error('Plugin must have a name');
}
if (this.plugins.has(plugin.name)) {
throw new Error(`Plugin ${plugin.name} is already registered`);
}
// Initialize plugin hooks
this.initializeHooks(plugin);
// Store plugin
this.plugins.set(plugin.name, plugin);
// Notify other plugins
this.communication.publish('pluginRegistered', { plugin });
return this;
}
// Initialize plugin hooks
initializeHooks(plugin) {
const hookNames = [
'buildStart', 'buildEnd', 'moduleParsed', 'resolveId',
'load', 'transform', 'generateBundle', 'writeBundle',
'renderChunk', 'renderStart', 'renderError'
];
hookNames.forEach(hookName => {
if (plugin[hookName]) {
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, []);
}
this.hooks.get(hookName).push({
plugin: plugin.name,
handler: plugin[hookName].bind(plugin)
});
}
});
}
// Execute hooks
async executeHook(hookName, ...args) {
const hooks = this.hooks.get(hookName) || [];
const results = [];
for (const { plugin, handler } of hooks) {
try {
const result = await handler(...args);
results.push({ plugin, result });
} catch (error) {
console.error(`Error in hook ${hookName} for plugin ${plugin}:`, error);
results.push({ plugin, error });
}
}
return results;
}
}
// 3. Plugin Lifecycle Manager
// lifecycle-manager.js
export class PluginLifecycleManager {
constructor(registry) {
this.registry = registry;
this.phases = new Map();
}
// Register a phase
registerPhase(name, plugins = []) {
this.phases.set(name, plugins);
return this;
}
// Execute a phase
async executePhase(name, context = {}) {
const phasePlugins = this.phases.get(name) || [];
for (const pluginName of phasePlugins) {
const plugin = this.registry.plugins.get(pluginName);
if (plugin && plugin.phaseHandler) {
await plugin.phaseHandler(name, context);
}
}
}
}
// ===== INTEGRATION WITH COMMON PLUGINS =====
// 4. Alias Resolution Plugin
// alias-resolver-plugin.js
export default function aliasResolverPlugin(options = {}) {
const { alias = {}, preserveSymlinks = false } = options;
return {
name: 'alias-resolver',
resolveId(id, importer) {
// Check if the ID matches any alias
for (const [key, value] of Object.entries(alias)) {
if (id === key || id.startsWith(key + '/')) {
const replacement = id.replace(key, value);
return this.resolve(replacement, importer, { preserveSymlinks });
}
}
return null;
},
// Integrate with node-resolve
async buildStart() {
// Ensure node-resolve is available
if (!this.getPluginInfo('node-resolve')) {
this.warn('alias-resolver works best with @rollup/plugin-node-resolve');
}
}
};
}
// 5. Virtual Modules Plugin
// virtual-modules-plugin.js
export default function virtualModulesPlugin(modules = {}) {
const virtualModulePrefix = '\0virtual:';
const virtualModules = new Map();
// Initialize with provided modules
Object.entries(modules).forEach(([id, content]) => {
virtualModules.set(virtualModulePrefix + id, content);
});
return {
name: 'virtual-modules',
resolveId(id) {
if (virtualModules.has(virtualModulePrefix + id)) {
return virtualModulePrefix + id;
}
return null;
},
load(id) {
if (virtualModules.has(id)) {
return virtualModules.get(id);
}
return null;
},
// API to add virtual modules at runtime
api: {
addModule(id, content) {
virtualModules.set(virtualModulePrefix + id, content);
},
hasModule(id) {
return virtualModules.has(virtualModulePrefix + id);
}
}
};
}
// 6. CSS Processing Plugin (with PostCSS integration)
// css-processor-plugin.js
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
export default function cssProcessorPlugin(options = {}) {
const {
plugins = [autoprefixer, cssnano],
inject = false,
outputName = 'styles.css',
minimize = process.env.NODE_ENV === 'production'
} = options;
let cssCode = '';
const processedCSS = new Set();
return {
name: 'css-processor',
transform(code, id) {
if (!id.endsWith('.css') && !id.endsWith('.scss') && !id.endsWith('.sass')) {
return null;
}
// Collect CSS code
cssCode += code + '\n';
return {
code: 'export default null;',
map: null
};
},
buildEnd() {
if (!cssCode.trim()) return;
return postcss(minimize ? [...plugins, cssnano] : plugins)
.process(cssCode, { from: undefined })
.then(result => {
const processedResult = result.css;
processedCSS.add(processedResult);
if (inject) {
// Create CSS injector
const injector = `
(function() {
var css = ${JSON.stringify(processedResult)};
if (typeof document !== 'undefined') {
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
}
})();
`;
this.emitFile({
type: 'chunk',
id: 'css-injector',
fileName: 'css-injector.js',
code: injector
});
} else {
// Emit CSS file
this.emitFile({
type: 'asset',
fileName: outputName,
source: processedResult
});
}
})
.catch(error => {
this.error(`CSS processing failed: ${error.message}`);
});
}
};
}
// 7. Environment Configuration Plugin
// environment-config-plugin.js
export default function environmentConfigPlugin(options = {}) {
const {
environments = {},
defaultEnv = 'development',
configFile = '.rollup.env.js'
} = options;
let currentEnv = defaultEnv;
let envConfig = {};
return {
name: 'environment-config',
buildStart() {
// Load environment configuration
const fs = require('fs');
const path = require('path');
if (fs.existsSync(configFile)) {
delete require.cache[require.resolve(path.resolve(configFile))];
const userConfig = require(path.resolve(configFile));
currentEnv = process.env.ROLLUP_ENV || userConfig.env || defaultEnv;
envConfig = { ...userConfig, ...environments[currentEnv] };
} else {
currentEnv = process.env.ROLLUP_ENV || defaultEnv;
envConfig = environments[currentEnv] || {};
}
console.log(`🌍 Using environment: ${currentEnv}`);
},
resolveId(id, importer) {
// Handle environment-specific modules
const envMatch = id.match(/^(.*)\.env\.(\w+)$/);
if (envMatch) {
const [, baseId, env] = envMatch;
if (env === currentEnv) {
return this.resolve(baseId, importer);
}
return null;
}
return null;
},
transform(code, id) {
// Replace environment variables in code
const envRegex = /__ENV_([A-Z_]+)__|__CURRENT_ENV__/g;
let hasReplacements = false;
const result = code.replace(envRegex, (match, varName) => {
hasReplacements = true;
if (varName) {
return JSON.stringify(envConfig[varName.toLowerCase()] || process.env[varName] || '');
}
return JSON.stringify(currentEnv);
});
if (hasReplacements) {
return {
code: result,
map: null
};
}
},
api: {
getEnv() {
return currentEnv;
},
getConfig() {
return envConfig;
},
isDevelopment() {
return currentEnv === 'development';
},
isProduction() {
return currentEnv === 'production';
}
}
};
}
// ===== ADVANCED ECOSYSTEM INTEGRATION =====
// 8. Plugin Dependency Manager
// dependency-manager-plugin.js
export default function dependencyManagerPlugin(options = {}) {
const { dependencies = {}, autoInstall = false } = options;
return {
name: 'dependency-manager',
buildStart() {
// Check plugin dependencies
for (const [pluginName, requiredVersion] of Object.entries(dependencies)) {
const pluginInfo = this.getPluginInfo(pluginName);
if (!pluginInfo) {
if (autoInstall) {
this.warn(`Installing missing plugin: ${pluginName}`);
// In a real implementation, you might want to install the plugin
} else {
this.error(`Required plugin not found: ${pluginName}`);
}
} else {
// Version check (simplified)
const version = pluginInfo.version || '0.0.0';
if (!this.satisfiesVersion(version, requiredVersion)) {
this.warn(`Plugin version mismatch: ${pluginName} requires ${requiredVersion}, found ${version}`);
}
}
}
},
satisfiesVersion(current, required) {
// Simplified version comparison
const currentParts = current.split('.').map(Number);
const requiredParts = required.split('.').map(Number);
for (let i = 0; i < Math.max(currentParts.length, requiredParts.length); i++) {
const currentPart = currentParts[i] || 0;
const requiredPart = requiredParts[i] || 0;
if (currentPart > requiredPart) return true;
if (currentPart < requiredPart) return false;
}
return true;
}
};
}
// 9. Plugin Configuration Validator
// config-validator-plugin.js
export default function configValidatorPlugin(options = {}) {
const {
requiredOptions = [],
optionValidators = {},
strict = false
} = options;
return {
name: 'config-validator',
buildStart() {
// Validate required options
for (const option of requiredOptions) {
if (!(option in this.options)) {
const message = `Required option '${option}' is missing`;
if (strict) {
this.error(message);
} else {
this.warn(message);
}
}
}
// Run custom validators
for (const [option, validator] of Object.entries(optionValidators)) {
if (option in this.options) {
try {
const result = validator(this.options[option]);
if (!result) {
this.warn(`Option '${option}' failed validation`);
}
} catch (error) {
this.warn(`Validation error for option '${option}': ${error.message}`);
}
}
}
}
};
}
// 10. Bundle Optimization Pipeline
// optimization-pipeline-plugin.js
export default function optimizationPipelinePlugin(options = {}) {
const {
stages = [],
parallel = false,
timeout = 30000
} = options;
return {
name: 'optimization-pipeline',
generateBundle(outputOptions, bundle) {
const pipeline = stages.map(stage => {
if (typeof stage === 'string') {
return this.getOptimizationStage(stage);
}
return stage;
});
let optimizedBundle = { ...bundle };
if (parallel) {
// Run stages in parallel
return Promise.all(
pipeline.map(stage => this.runOptimizationStage(stage, optimizedBundle))
).then(() => {
// Merge results
this.mergeOptimizationResults(optimizedBundle, pipeline);
});
} else {
// Run stages sequentially
return pipeline.reduce((promise, stage) => {
return promise.then(() => this.runOptimizationStage(stage, optimizedBundle));
}, Promise.resolve());
}
},
getOptimizationStage(name) {
const stages = {
'dedupe': this.dedupeStage.bind(this),
'compress': this.compressStage.bind(this),
'mangle': this.mangleStage.bind(this),
'treeshake': this.treeshakeStage.bind(this),
'optimize-imports': this.optimizeImportsStage.bind(this)
};
return stages[name] || (() => Promise.resolve());
},
runOptimizationStage(stage, bundle) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Optimization stage timeout'));
}, timeout);
try {
const result = stage(bundle);
if (result && typeof result.then === 'function') {
result.finally(() => clearTimeout(timer)).then(resolve).catch(reject);
} else {
clearTimeout(timer);
resolve();
}
} catch (error) {
clearTimeout(timer);
reject(error);
}
});
},
// Optimization stage implementations
dedupeStage(bundle) {
// Remove duplicate modules
const seen = new Set();
const deduped = {};
for (const [fileName, chunk] of Object.entries(bundle)) {
const hash = this.createChunkHash(chunk);
if (!seen.has(hash)) {
seen.add(hash);
deduped[fileName] = chunk;
}
}
// Update bundle in place
Object.keys(bundle).forEach(key => delete bundle[key]);
Object.assign(bundle, deduped);
},
compressStage(bundle) {
// Compress chunks (simplified)
for (const chunk of Object.values(bundle)) {
if (chunk.type === 'chunk' && chunk.code) {
// Basic compression - in practice, you'd use a proper minifier
chunk.code = chunk.code
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove comments
.replace(/\s+/g, ' ') // Collapse whitespace
.trim();
}
}
},
createChunkHash(chunk) {
const crypto = require('crypto');
const content = chunk.code || chunk.source || '';
return crypto.createHash('md5').update(content).digest('hex');
}
};
}
// ===== ROLLUP CONFIGURATION WITH ECOSYSTEM INTEGRATION =====
// rollup.config.js - Complete ecosystem example
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import json from '@rollup/plugin-json';
import { terser } from 'rollup-plugin-terser';
import alias from '@rollup/plugin-alias';
// Import custom plugins
import aliasResolverPlugin from './plugins/alias-resolver-plugin.js';
import virtualModulesPlugin from './plugins/virtual-modules-plugin.js';
import cssProcessorPlugin from './plugins/css-processor-plugin.js';
import environmentConfigPlugin from './plugins/environment-config-plugin.js';
import dependencyManagerPlugin from './plugins/dependency-manager-plugin.js';
import configValidatorPlugin from './plugins/config-validator-plugin.js';
import optimizationPipelinePlugin from './plugins/optimization-pipeline-plugin.js';
// Virtual modules
const virtualModules = {
'config': `
export const API_URL = __ENV_API_URL__;
export const VERSION = '__CURRENT_ENV__-v1.0.0';
`,
'constants': `
export const APP_NAME = 'My App';
export const BUILD_DATE = new Date().toISOString();
`
};
export default {
input: 'src/index.js',
output: [
{
file: 'dist/bundle.esm.js',
format: 'es',
sourcemap: true
},
{
file: 'dist/bundle.cjs.js',
format: 'cjs',
sourcemap: true
},
{
file: 'dist/bundle.umd.js',
format: 'umd',
name: 'MyApp',
sourcemap: true
}
],
plugins: [
// Configuration validation
configValidatorPlugin({
requiredOptions: ['input'],
optionValidators: {
input: (value) => typeof value === 'string',
output: (value) => Array.isArray(value) || typeof value === 'object'
},
strict: true
}),
// Dependency management
dependencyManagerPlugin({
dependencies: {
'node-resolve': '^13.0.0',
'typescript': '^8.0.0'
},
autoInstall: false
}),
// Environment configuration
environmentConfigPlugin({
environments: {
development: {
API_URL: 'http://localhost:3000/api',
DEBUG: true
},
production: {
API_URL: 'https://api.myapp.com',
DEBUG: false
},
test: {
API_URL: 'http://test-api:3000/api',
DEBUG: true
}
}
}),
// Core plugins
nodeResolve({
browser: true,
preferBuiltins: false
}),
commonjs(),
typescript(),
json(),
// Alias resolution
alias({
entries: [
{ find: '@', replacement: './src' },
{ find: '@utils', replacement: './src/utils' },
{ find: '@components', replacement: './src/components' }
]
}),
// Custom alias resolver (works with node-resolve)
aliasResolverPlugin({
alias: {
'react': 'preact/compat',
'react-dom': 'preact/compat'
}
}),
// Virtual modules
virtualModulesPlugin(virtualModules),
// CSS processing
cssProcessorPlugin({
inject: process.env.NODE_ENV === 'development',
minimize: process.env.NODE_ENV === 'production',
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default'
})
]
}),
// Optimization pipeline
optimizationPipelinePlugin({
stages: ['dedupe', 'optimize-imports', 'compress'],
parallel: true
}),
// Production optimizations
process.env.NODE_ENV === 'production' && terser({
format: {
comments: /^!/,
ascii_only: true
},
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log', 'console.info'],
passes: 2
},
mangle: {
properties: {
regex: /^_/
}
}
})
].filter(Boolean)
};
// Export for use in other files
export {
aliasResolverPlugin,
virtualModulesPlugin,
cssProcessorPlugin,
environmentConfigPlugin,
dependencyManagerPlugin,
configValidatorPlugin,
optimizationPipelinePlugin
};