🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Développement de Plugins Rollup
Exemples de développement de plugins Rollup incluant des plugins personnalisés, des transformations et des patterns de bundling
💻 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;
💻 Écosystème de Plugins Rollup javascript
🟡 intermediate
⭐⭐⭐⭐
Création de plugins qui s'intègrent avec l'écosystème Rollup plus large et patterns communs
⏱️ 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
};