Outil de Construction Grunt

Exemples du task runner JavaScript Grunt incluant l'automatisation, les processus de construction et les workflows de déploiement

Key Facts

Category
Build Tools
Items
3
Format Families
sample

Sample Overview

Exemples du task runner JavaScript Grunt incluant l'automatisation, les processus de construction et les workflows de déploiement This sample set belongs to Build Tools and can be used to test related workflows inside Elysia Tools.

💻 Tâches de Base Grunt javascript

🟢 simple ⭐⭐

Tâches essentielles de Grunt pour le développement web moderne incluant le traitement de fichiers, la surveillance et la construction

⏱️ 20 min 🏷️ grunt, automation, build
Prerequisites: Node.js knowledge, JavaScript basics
// Grunt Basic Tasks Setup
// Modern Grunt configuration for web development workflows

// ===== package.json =====
{
  "name": "my-grunt-project",
  "version": "1.0.0",
  "description": "Modern web application using Grunt for task automation",
  "main": "index.js",
  "scripts": {
    "dev": "grunt dev",
    "build": "grunt build",
    "watch": "grunt watch",
    "clean": "grunt clean",
    "serve": "grunt serve",
    "test": "grunt test",
    "lint": "grunt lint",
    "deploy": "grunt deploy"
  },
  "devDependencies": {
    "grunt": "^1.6.1",
    "grunt-cli": "^1.4.3",
    "grunt-contrib-sass": "^2.0.0",
    "grunt-contrib-watch": "^1.1.0",
    "grunt-contrib-clean": "^2.0.1",
    "grunt-contrib-copy": "^1.0.0",
    "grunt-contrib-concat": "^2.1.0",
    "grunt-contrib-uglify": "^5.2.2",
    "grunt-contrib-cssmin": "^4.0.0",
    "grunt-contrib-imagemin": "^4.0.0",
    "grunt-contrib-htmlmin": "^3.1.0",
    "grunt-contrib-compress": "^2.0.0",
    "grunt-browser-sync": "^2.2.0",
    "grunt-eslint": "^24.3.0",
    "grunt-stylelint": "^0.18.0",
    "grunt-postcss": "^0.9.0",
    "grunt-autoprefixer": "^4.0.0",
    "grunt-sass-lint": "^0.2.4",
    "grunt-babel": "^8.0.0",
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/preset-react": "^7.18.0",
    "grunt-filerev": "^4.0.0",
    "grunt-usemin": "^3.1.1",
    "grunt-replace": "^2.0.2",
    "grunt-newer": "^1.3.0",
    "grunt-notify": "^0.4.5",
    "grunt-shell": "^4.0.0",
    "grunt-run": "^0.8.1",
    "grunt-string-replace": "^1.3.1",
    "grunt-banner": "^0.6.7",
    "grunt-text-replace": "^0.4.0",
    "load-grunt-tasks": "^5.1.0",
    "time-grunt": "^2.0.0",
    "jit-grunt": "^0.10.0",
    "dotenv": "^16.0.0"
  }
}

// ===== Gruntfile.js =====
module.exports = function(grunt) {
    'use strict';

    // Load environment variables
    require('dotenv').config();

    // Measure build time
    require('time-grunt')(grunt);

    // Load grunt tasks automatically
    require('jit-grunt')(grunt, {
        'babel': 'grunt-babel',
        'stylelint': 'grunt-stylelint',
        'sasslint': 'grunt-sass-lint'
    });

    // Project configuration
    grunt.initConfig({
        // Project settings
        pkg: grunt.file.readJSON('package.json'),

        // Environment settings
        env: process.env,

        // Directory structure
        dirs: {
            src: 'src',
            dist: 'dist',
            tmp: '.tmp',
            css: 'src/scss',
            js: 'src/js',
            images: 'src/images',
            fonts: 'src/fonts',
            vendor: 'src/vendor'
        },

        // Clean task
        clean: {
            dist: {
                src: ['<%= dirs.dist %>/**/*', '!<%= dirs.dist %>/.gitkeep']
            },
            tmp: {
                src: ['<%= dirs.tmp %>/**/*']
            },
            server: {
                src: ['.sass-cache']
            }
        },

        // Copy task
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= dirs.src %>',
                    dest: '<%= dirs.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.{webp,gif}',
                        'fonts/**/*',
                        'vendor/**/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                dot: true,
                cwd: '<%= dirs.src %>',
                dest: '<%= dirs.dist %>',
                src: 'css/**/*.css'
            }
        },

        // Sass/SCSS compilation
        sass: {
            options: {
                implementation: require('sass'),
                sourceMap: true,
                outputStyle: 'expanded'
            },
            dist: {
                files: {
                    '<%= dirs.tmp %>/css/main.css': '<%= dirs.css %>/main.scss',
                    '<%= dirs.tmp %>/css/components.css': '<%= dirs.css %>/components.scss'
                }
            },
            server: {
                options: {
                    sourceMap: true
                },
                files: {
                    '<%= dirs.tmp %>/css/main.css': '<%= dirs.css %>/main.scss'
                }
            }
        },

        // PostCSS processing (autoprefixer)
        postcss: {
            options: {
                processors: [
                    require('autoprefixer')({
                        overrideBrowserslist: ['last 2 versions', 'not dead']
                    })
                ]
            },
            dist: {
                src: '<%= dirs.tmp %>/css/*.css'
            }
        },

        // CSS minification
        cssmin: {
            options: {
                level: 2,
                compatibility: 'ie8'
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= dirs.tmp %>/css',
                    dest: '<%= dirs.dist %>/css',
                    src: ['*.css', '!*.min.css'],
                    ext: '.min.css'
                }]
            }
        },

        // JavaScript concatenation
        concat: {
            options: {
                separator: ';'
            },
            dist: {
                src: [
                    '<%= dirs.js %>/libs/jquery.js',
                    '<%= dirs.js %>/libs/bootstrap.js',
                    '<%= dirs.js %>/plugins/*.js',
                    '<%= dirs.js %>/main.js'
                ],
                dest: '<%= dirs.tmp %>/js/scripts.js'
            }
        },

        // Babel transpilation
        babel: {
            options: {
                sourceMap: true,
                presets: [
                    ['@babel/preset-env', {
                        targets: {
                            browsers: ['last 2 versions', 'not dead']
                        }
                    }]
                ]
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= dirs.js %>',
                    src: ['**/*.js', '!libs/**/*.js', '!vendor/**/*.js'],
                    dest: '<%= dirs.tmp %>/js'
                }]
            }
        },

        // JavaScript minification
        uglify: {
            options: {
                banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
                mangle: true,
                compress: {
                    drop_console: true,
                    drop_debugger: true
                }
            },
            dist: {
                files: {
                    '<%= dirs.dist %>/js/scripts.min.js': ['<%= dirs.tmp %>/js/scripts.js']
                }
            }
        },

        // HTML minification
        htmlmin: {
            dist: {
                options: {
                    collapseWhitespace: true,
                    conservativeCollapse: true,
                    collapseBooleanAttributes: true,
                    removeCommentsFromCDATA: true,
                    removeOptionalTags: true,
                    removeRedundantAttributes: true,
                    removeScriptTypeAttributes: true,
                    removeStyleLinkTypeAttributes: true,
                    useShortDoctype: true,
                    minifyCSS: true,
                    minifyJS: true
                },
                files: [{
                    expand: true,
                    cwd: '<%= dirs.src %>',
                    src: '**/*.html',
                    dest: '<%= dirs.dist %>'
                }]
            }
        },

        // Image optimization
        imagemin: {
            dist: {
                options: {
                    optimizationLevel: 7,
                    progressive: true,
                    interlaced: true,
                    svgoPlugins: [{ removeViewBox: false }]
                },
                files: [{
                    expand: true,
                    cwd: '<%= dirs.images %>',
                    src: ['**/*.{png,jpg,jpeg,gif,svg}'],
                    dest: '<%= dirs.dist %>/images'
                }]
            }
        },

        // File versioning
        filerev: {
            options: {
                encoding: 'utf8',
                algorithm: 'md5',
                length: 8
            },
            dist: {
                src: [
                    '<%= dirs.dist %>/js/*.js',
                    '<%= dirs.dist %>/css/*.css',
                    '<%= dirs.dist %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}',
                    '<%= dirs.dist %>/fonts/**/*.{eot,svg,ttf,woff,woff2}'
                ]
            }
        },

        // Usemin for HTML asset replacement
        useminPrepare: {
            html: '<%= dirs.src %>/index.html',
            options: {
                dest: '<%= dirs.dist %>',
                flow: {
                    html: {
                        steps: {
                            js: ['concat', 'uglifyjs'],
                            css: ['cssmin']
                        },
                        post: {}
                    }
                }
            }
        },

        usemin: {
            html: ['<%= dirs.dist %>/**/*.html'],
            css: ['<%= dirs.dist %>/css/**/*.css'],
            options: {
                assetsDirs: [
                    '<%= dirs.dist %>',
                    '<%= dirs.dist %>/images',
                    '<%= dirs.dist %>/css'
                ]
            }
        },

        // ESLint for JavaScript
        eslint: {
            options: {
                configFile: '.eslintrc.json',
                failOnError: false,
                quiet: true
            },
            target: ['<%= dirs.js %>/**/*.js', '!<%= dirs.js %>/vendor/**/*']
        },

        // Stylelint for CSS/SCSS
        stylelint: {
            all: {
                src: ['<%= dirs.css %>/**/*.scss'],
                options: {
                    configFile: '.stylelintrc.json',
                    failOnError: false,
                    quiet: true
                }
            }
        },

        // Sass linting
        sasslint: {
            allFiles: {
                src: ['<%= dirs.css %>/**/*.scss'],
                options: {
                    configFile: '.sass-lint.yml'
                }
            }
        },

        // Banner for files
        banner: {
            dist: {
                options: {
                    banner: '/*! <%= pkg.name %> <%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> */\n'
                },
                files: {
                    src: ['<%= dirs.dist %>/css/*.css', '<%= dirs.dist %>/js/*.js']
                }
            }
        },

        // File replacement
        replace: {
            dist: {
                options: {
                    patterns: [{
                        match: /VERSION/g,
                        replacement: '<%= pkg.version %>'
                    }, {
                        match: /BUILD_DATE/g,
                        replacement: '<%= grunt.template.today("yyyy-mm-dd") %>'
                    }]
                },
                files: [{
                    expand: true,
                    flatten: true,
                    src: ['<%= dirs.dist %>/**/*.html'],
                    dest: '<%= dirs.dist %>'
                }]
            }
        },

        // Browser sync for development
        browserSync: {
            dev: {
                bsFiles: {
                    src: [
                        '<%= dirs.tmp %>/css/**/*.css',
                        '<%= dirs.tmp %>/js/**/*.js',
                        '<%= dirs.src %>/**/*.html'
                    ]
                },
                options: {
                    watchTask: true,
                    server: {
                        baseDir: ['<%= dirs.tmp %>', '<%= dirs.src %>'],
                        routes: {
                            '/bower_components': 'bower_components'
                        }
                    },
                    port: 3000,
                    open: true,
                    notify: false
                }
            }
        },

        // Watch task
        watch: {
            options: {
                livereload: true,
                spawn: false
            },
            sass: {
                files: ['<%= dirs.css %>/**/*.{scss,sass}'],
                tasks: ['sass:server', 'postcss']
            },
            js: {
                files: ['<%= dirs.js %>/**/*.js', '!<%= dirs.js %>/vendor/**/*'],
                tasks: ['eslint', 'babel']
            },
            html: {
                files: ['<%= dirs.src %>/**/*.html'],
                tasks: ['copy:html']
            },
            images: {
                files: ['<%= dirs.images %>/**/*.{png,jpg,jpeg,gif,webp,svg}'],
                tasks: ['newer:imagemin']
            }
        },

        // Shell commands
        shell: {
            testUnit: {
                command: 'npm run test:unit'
            },
            testE2E: {
                command: 'npm run test:e2e'
            },
            buildProduction: {
                command: 'NODE_ENV=production npm run build'
            }
        },

        // Notify for build completion
        notify: {
            buildComplete: {
                options: {
                    title: 'Build Complete',
                    message: 'Build completed successfully!'
                }
            },
            watchStart: {
                options: {
                    title: 'Grunt Watch',
                    message: 'Watching files for changes...'
                }
            }
        },

        // ZIP creation
        compress: {
            dist: {
                options: {
                    archive: 'dist-<%= pkg.version %>.zip',
                    mode: 'zip',
                    level: 9,
                    pretty: true
                },
                files: [{
                    src: ['**'],
                    cwd: '<%= dirs.dist %>',
                    expand: true
                }]
            }
        }
    });

    // Development task
    grunt.registerTask('dev', [
        'clean:tmp',
        'clean:server',
        'sass:server',
        'postcss',
        'babel',
        'browserSync',
        'watch',
        'notify:watchStart'
    ]);

    // Build task
    grunt.registerTask('build', [
        'clean:dist',
        'clean:tmp',
        'sass:dist',
        'postcss',
        'babel',
        'concat',
        'copy',
        'cssmin',
        'uglify',
        'htmlmin',
        'imagemin',
        'filerev',
        'usemin',
        'banner:dist',
        'replace:dist',
        'notify:buildComplete'
    ]);

    // Test task
    grunt.registerTask('test', [
        'eslint',
        'stylelint',
        'sasslint',
        'shell:testUnit'
    ]);

    // Lint task
    grunt.registerTask('lint', [
        'eslint',
        'stylelint',
        'sasslint'
    ]);

    // Default task
    grunt.registerTask('default', [
        'build'
    ]);

    // Serve task
    grunt.registerTask('serve', [
        'dev'
    ]);

    // Deploy task
    grunt.registerTask('deploy', [
        'test',
        'build',
        'compress:dist'
    ]);

    // Custom task: Build stats
    grunt.registerTask('build-stats', 'Display build statistics', function() {
        var distDir = grunt.config('dirs.dist');
        var fs = require('fs');
        var path = require('path');
        var stats = {
            files: 0,
            totalSize: 0,
            extensions: {}
        };

        function processDirectory(dir) {
            var files = fs.readdirSync(dir);

            files.forEach(function(file) {
                var filePath = path.join(dir, file);
                var stat = fs.statSync(filePath);

                if (stat.isDirectory()) {
                    processDirectory(filePath);
                } else {
                    stats.files++;
                    stats.totalSize += stat.size;

                    var ext = path.extname(file).toLowerCase();
                    if (ext) {
                        stats.extensions[ext] = (stats.extensions[ext] || 0) + 1;
                    }
                }
            });
        }

        if (fs.existsSync(distDir)) {
            processDirectory(distDir);
        }

        grunt.log.writeln('Build Statistics:');
        grunt.log.writeln('----------------');
        grunt.log.writeln('Total files: ' + stats.files);
        grunt.log.writeln('Total size: ' + (stats.totalSize / 1024).toFixed(2) + ' KB');
        grunt.log.writeln('Extensions:');

        Object.keys(stats.extensions).forEach(function(ext) {
            grunt.log.writeln('  ' + ext + ': ' + stats.extensions[ext]);
        });
    });

    // Custom task: Environment check
    grunt.registerTask('env-check', 'Check environment requirements', function() {
        var nodeVersion = process.version;
        var npmVersion = require('child_process')
            .execSync('npm --version', { encoding: 'utf8' })
            .trim();

        grunt.log.writeln('Environment Check:');
        grunt.log.writeln('-----------------');
        grunt.log.writeln('Node.js: ' + nodeVersion);
        grunt.log.writeln('npm: ' + npmVersion);
        grunt.log.writeln('Environment: ' + (grunt.config('env.NODE_ENV') || 'development'));

        // Check required versions
        var requiredNode = '>=14.0.0';
        if (require('semver').satisfies(nodeVersion, requiredNode)) {
            grunt.log.ok('Node.js version satisfies requirement: ' + requiredNode);
        } else {
            grunt.log.error('Node.js version does not satisfy requirement: ' + requiredNode);
        }
    });
};

💻 Workflows Avancés Grunt javascript

🟡 intermediate ⭐⭐⭐⭐

Workflows de Grunt avancés incluant les constructions multi-cibles, l'optimisation et les stratégies de déploiement

⏱️ 35 min 🏷️ grunt, workflows, optimization
Prerequisites: Grunt basics, JavaScript optimization, Build processes
// Grunt Advanced Workflows
// Complex build processes, optimization, and deployment automation

module.exports = function(grunt) {
    'use strict';

    // Load all grunt tasks
    require('load-grunt-tasks')(grunt);

    // Load environment-specific configuration
    var envConfig = grunt.file.exists('grunt-config.js')
        ? require('./grunt-config')(grunt)
        : {};

    // Merge configurations
    grunt.util._.merge(grunt.config(), envConfig);

    // Advanced configuration
    grunt.initConfig({
        // Multi-environment configuration
        environments: {
            development: {
                dirs: {
                    src: 'src',
                    dist: 'dist-dev',
                    tmp: '.tmp-dev'
                },
                optimization: false,
                sourcemaps: true,
                minify: false,
                watch: true,
                livereload: true
            },
            staging: {
                dirs: {
                    src: 'src',
                    dist: 'dist-staging',
                    tmp: '.tmp-staging'
                },
                optimization: true,
                sourcemaps: true,
                minify: true,
                watch: false,
                livereload: false
            },
            production: {
                dirs: {
                    src: 'src',
                    dist: 'dist-prod',
                    tmp: '.tmp-prod'
                },
                optimization: true,
                sourcemaps: false,
                minify: true,
                watch: false,
                livereload: false
            }
        },

        // Dynamic configuration based on environment
        env: grunt.option('env') || 'development',
        config: grunt.config('environments.<%= env %>'),

        // Advanced clean task with patterns
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '<%= config.dirs.dist %>/**/*',
                        '!<%= config.dirs.dist %>/.gitkeep'
                    ]
                }]
            },
            temp: {
                files: [{
                    dot: true,
                    src: [
                        '<%= config.dirs.tmp %>/**/*',
                        '.sass-cache/**/*',
                        '.tmp/**/*'
                    ]
                }]
            },
            legacy: {
                files: [{
                    src: [
                        'dist/**/*',
                        'tmp/**/*',
                        '.cache/**/*'
                    ]
                }]
            }
        },

        // Multi-target sass compilation
        sass: {
            options: {
                implementation: require('sass'),
                sourceMap: '<%= config.sourcemaps %>',
                outputStyle: '<%= config.minify ? "compressed" : "expanded" %>'
            },
            main: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/scss',
                    src: ['**/*.scss', '!**/_*.scss'],
                    dest: '<%= config.dirs.tmp %>/css',
                    ext: '.css'
                }]
            },
            critical: {
                options: {
                    outputStyle: 'expanded'
                },
                files: {
                    '<%= config.dirs.tmp %>/css/critical.css':
                        '<%= config.dirs.src %>/scss/critical.scss'
                }
            },
            themes: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/scss/themes',
                    src: ['**/*.scss'],
                    dest: '<%= config.dirs.tmp %>/css/themes',
                    ext: '.css'
                }]
            }
        },

        // Advanced PostCSS with multiple processors
        postcss: {
            options: {
                processors: [
                    require('autoprefixer')({
                        overrideBrowserslist: ['last 2 versions', 'not dead']
                    }),
                    require('postcss-flexbugs-fixes')(),
                    require('postcss-preset-env')({
                        stage: 2,
                        features: {
                            'custom-properties': true,
                            'nesting': true,
                            'calc': true
                        }
                    })
                ].concat(
                    grunt.config('config.minify')
                    ? [require('cssnano')({ preset: 'default' })]
                    : []
                )
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.tmp %>/css',
                    src: ['**/*.css'],
                    dest: '<%= config.dirs.tmp %>/css'
                }]
            }
        },

        // Advanced Babel with environment-specific presets
        babel: {
            options: {
                sourceMap: '<%= config.sourcemaps %>',
                presets: [
                    ['@babel/preset-env', {
                        targets: {
                            browsers: grunt.config('config.env') === 'production'
                                ? ['> 0.5%', 'not dead']
                                : ['last 2 versions']
                        },
                        modules: false,
                        useBuiltIns: 'usage',
                        corejs: 3
                    }]
                ],
                plugins: [
                    '@babel/plugin-proposal-class-properties',
                    '@babel/plugin-proposal-object-rest-spread'
                ].concat(
                    grunt.config('config.env') === 'production'
                    ? ['@babel/plugin-transform-runtime']
                    : []
                )
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/js',
                    src: ['**/*.js', '!**/*.min.js'],
                    dest: '<%= config.dirs.tmp %>/js'
                }]
            }
        },

        // Advanced concatenation with multiple targets
        concat: {
            options: {
                separator: grunt.config('config.minify') ? ';' : ';\n',
                process: function(src, filepath) {
                    return '// Source: ' + filepath + '\n' + src;
                }
            },
            vendor: {
                files: {
                    '<%= config.dirs.tmp %>/js/vendor.js': [
                        '<%= config.dirs.src %>/js/libs/jquery.js',
                        '<%= config.dirs.src %>/js/libs/bootstrap.js',
                        '<%= config.dirs.src %>/js/libs/modernizr.js'
                    ]
                }
            },
            app: {
                files: {
                    '<%= config.dirs.tmp %>/js/app.js': [
                        '<%= config.dirs.tmp %>/js/app/**/*.js',
                        '!<%= config.dirs.tmp %>/js/app/vendor.js'
                    ]
                }
            },
            themes: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/js/themes',
                    src: ['**/*.js'],
                    dest: '<%= config.dirs.tmp %>/js/themes',
                    ext: '.bundle.js'
                }]
            }
        },

        // Advanced uglify with multiple configurations
        uglify: {
            options: {
                banner: '/*! <%= pkg.name %> <%= pkg.version %> - ' +
                       grunt.template.today('yyyy-mm-dd') + ' */\n',
                sourceMap: '<%= config.sourcemaps %>',
                sourceMapIncludeSources: true,
                sourceMapRoot: '../../<%= config.dirs.src %>/js',
                mangle: grunt.config('config.minify'),
                compress: {
                    drop_console: grunt.config('config.minify'),
                    drop_debugger: grunt.config('config.minify'),
                    dead_code: true,
                    unused: true,
                    warnings: false
                },
                output: {
                    comments: grunt.config('config.minify') ? false : /^!/
                }
            },
            vendor: {
                files: [{
                    '<%= config.dirs.dist %>/js/vendor.min.js':
                        '<%= concat.vendor.files[0]["<%= config.dirs.tmp %>/js/vendor.js"] %>'
                }]
            },
            app: {
                files: [{
                    '<%= config.dirs.dist %>/js/app.min.js':
                        '<%= concat.app.files[0]["<%= config.dirs.tmp %>/js/app.js"] %>'
                }]
            },
            themes: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.tmp %>/js/themes',
                    src: ['**/*.bundle.js'],
                    dest: '<%= config.dirs.dist %>/js/themes',
                    ext: '.min.js'
                }]
            }
        },

        // Critical CSS extraction
        critical: {
            options: {
                base: '<%= config.dirs.dist %>/',
                css: [
                    '<%= config.dirs.dist %>/css/main.min.css'
                ],
                width: 1200,
                height: 900,
                minify: grunt.config('config.minify'),
                extract: true,
                ignore: []
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>',
                    src: ['**/*.html', '!**/_*.html'],
                    dest: '<%= config.dirs.dist %>'
                }]
            }
        },

        // Advanced image optimization with different strategies
        imagemin: {
            options: {
                cache: false,
                optimizationLevel: grunt.config('config.optimization') ? 7 : 3,
                progressive: true,
                interlaced: true
            },
            static: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/images',
                    src: ['**/*.{png,jpg,jpeg,gif,svg}'],
                    dest: '<%= config.dirs.dist %>/images'
                }]
            },
            dynamic: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/images/uploads',
                    src: ['**/*.{png,jpg,jpeg,gif,svg,webp}'],
                    dest: '<%= config.dirs.dist %>/images/uploads'
                }]
            },
            icons: {
                options: {
                    optimizationLevel: 7,
                    svgoPlugins: [{
                        removeViewBox: false,
                        removeEmptyAttrs: false
                    }]
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/icons',
                    src: ['**/*.svg'],
                    dest: '<%= config.dirs.dist %>/icons'
                }]
            }
        },

        // Sprite generation
        sprite: {
            options: {
                spritemap: '<%= config.dirs.src %>/images/sprite/**/*.png',
                spritesmithPath: require('spritesmith'),
                engine: 'pixelsmith',
                algorithm: 'binary-tree',
                padding: 2
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>/scss',
                    src: ['*.scss'],
                    dest: '<%= config.dirs.tmp %>/scss',
                    rename: function(name) {
                        return '_' + name;
                    }
                }],
                dest: {
                    '<%= config.dirs.tmp %>/images': 'sprite.png'
                }
            }
        },

        // Advanced HTML processing
        processhtml: {
            options: {
                includeBase: '<%= config.dirs.src %>/',
                commentMarker: 'build',
                strip: true,
                data: {
                    version: '<%= pkg.version %>',
                    buildDate: grunt.template.today('yyyy-mm-dd'),
                    environment: grunt.config('config.env')
                }
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.src %>',
                    src: ['**/*.html', '!**/_*.html'],
                    dest: '<%= config.dirs.dist %>'
                }]
            }
        },

        // Advanced file versioning
        filerev: {
            options: {
                algorithm: 'sha256',
                length: 12
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.dist %>',
                    src: [
                        'js/**/*.js',
                        'css/**/*.css',
                        'images/**/*.{png,jpg,jpeg,gif,webp,svg}',
                        'fonts/**/*.{eot,svg,ttf,woff,woff2}'
                    ]
                }]
            }
        },

        // Asset rewriting
        replace: {
            assets: {
                options: {
                    patterns: [{
                        match: /assets\/images\//g,
                        replacement: 'assets/images/'
                    }, {
                        match: /assets\/css\//g,
                        replacement: function(match) {
                            return grunt.filerev.replacements[match] || match;
                        }
                    }, {
                        match: /assets\/js\//g,
                        replacement: function(match) {
                            return grunt.filerev.replacements[match] || match;
                        }
                    }]
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.dist %>',
                    src: ['**/*.html', '**/*.css', '**/*.js']
                }]
            }
        },

        // Cache busting with file hashes
        cacheBust: {
            options: {
                baseDir: '<%= config.dirs.dist %>',
                assets: ['js/**/*.js', 'css/**/*.css'],
                createCopies: false
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.dirs.dist %>',
                    src: ['**/*.html']
                }]
            }
        },

        // Progressive enhancement for IE
        legacy: {
            ie8: {
                options: {
                    dest: '<%= config.dirs.dist %>/legacy',
                    minify: false
                },
                files: {
                    '<%= config.dirs.dist %>/legacy/js/app.ie8.js':
                        '<%= config.dirs.src %>/js/ie/app.ie8.js'
                }
            }
        },

        // Advanced watch with livereload and reload on different events
        watch: {
            options: {
                livereload: {
                    port: 35729,
                    hostname: 'localhost'
                },
                spawn: false,
                interval: 1000
            },
            sass: {
                files: ['<%= config.dirs.src %>/scss/**/*.{scss,sass}'],
                tasks: ['sass', 'postcss'],
                options: {
                    livereload: true
                }
            },
            js: {
                files: ['<%= config.dirs.src %>/js/**/*.js', '!**/*.min.js'],
                tasks: ['babel', 'concat', 'uglify'],
                options: {
                    livereload: true
                }
            },
            images: {
                files: ['<%= config.dirs.src %>/images/**/*.{png,jpg,jpeg,gif,svg,webp}'],
                tasks: ['newer:imagemin'],
                options: {
                    livereload: true
                }
            },
            config: {
                files: ['Gruntfile.js', 'grunt-config.js'],
                tasks: ['config-reload'],
                options: {
                    reload: true
                }
            }
        },

        // Code quality tasks
        eslint: {
            options: {
                configFile: '.eslintrc.json',
                fix: grunt.config('config.env') === 'development',
                cache: true,
                quiet: true
            },
            target: [
                '<%= config.dirs.src %>/js/**/*.js',
                '!<%= config.dirs.src %>/js/vendor/**/*',
                '!<%= config.dirs.src %>/js/libs/**/*'
            ]
        },

        // Performance budgeting
        filesize: {
            options: {
                gzip: true,
                showFiles: true,
                showSizes: true
            },
            js: {
                files: '<%= config.dirs.dist %>/js/**/*.js'
            },
            css: {
                files: '<%= config.dirs.dist %>/css/**/*.css'
            }
        },

        // Bundle analysis
        bundlesize: {
            options: {
                maxSize: '200kb',
                gzip: true
            },
            main: {
                src: '<%= config.dirs.dist %>/js/app.min.js'
            }
        },

        // Custom tasks and workflows
        concurrent: {
            options: {
                logConcurrentOutput: true
            },
            dev: ['watch', 'connect'],
            build: ['sass', 'babel', 'imagemin'],
            deploy: ['build', 'test']
        },

        // Server configuration
        connect: {
            options: {
                port: 9000,
                hostname: 'localhost',
                base: '<%= config.dirs.tmp %>',
                livereload: true,
                open: true,
                middleware: function(connect, options, middlewares) {
                    // Custom middleware for API proxy
                    var proxy = require('grunt-connect-proxy/lib/utils').proxyRequest;
                    middlewares.unshift(proxy);
                    return middlewares;
                }
            },
            proxies: [{
                context: '/api',
                host: 'localhost',
                port: 8080,
                https: false,
                changeOrigin: false,
                xforward: false
            }],
            livereload: {
                options: {
                    base: '<%= config.dirs.tmp %>',
                    open: {
                        target: 'http://localhost:9000'
                    }
                }
            }
        }
    });

    // Environment-specific task registration
    grunt.registerTask('build:dev', function() {
        grunt.option('env', 'development');
        grunt.task.run(['build']);
    });

    grunt.registerTask('build:staging', function() {
        grunt.option('env', 'staging');
        grunt.task.run(['build']);
    });

    grunt.registerTask('build:prod', function() {
        grunt.option('env', 'production');
        grunt.task.run(['build']);
    });

    // Main build task
    grunt.registerTask('build', 'Build the application', function() {
        var tasks = [
            'clean',
            'sass',
            'postcss',
            'sprite',
            'babel',
            'concat',
            'copy',
            'imagemin',
            'eslint',
            'processhtml',
            'cacheBust'
        ];

        if (grunt.config('config.minify')) {
            tasks.push('cssmin', 'uglify');
        }

        tasks.push('replace:assets', 'filerev');

        grunt.task.run(tasks);
    });

    // Development workflow
    grunt.registerTask('dev', [
        'clean:tmp',
        'build:dev',
        'connect',
        'watch'
    ]);

    // Testing workflow
    grunt.registerTask('test', [
        'eslint',
        'bundlesize',
        'filesize',
        'qunit'
    ]);

    // Deployment workflow
    grunt.registerTask('deploy', [
        'test',
        'build:prod',
        'compress'
    ]);

    // Custom task: Performance audit
    grunt.registerTask('performance-audit', 'Run performance audit on built assets', function() {
        var fs = require('fs');
        var path = require('path');
        var distDir = grunt.config('config.dirs.dist');

        var audit = {
            assets: [],
            totalSize: 0,
            recommendations: []
        };

        function analyzeFile(filePath, type) {
            var stats = fs.statSync(filePath);
            var sizeKB = (stats.size / 1024).toFixed(2);

            audit.assets.push({
                type: type,
                path: path.relative(distDir, filePath),
                sizeKB: parseFloat(sizeKB),
                sizeRaw: stats.size
            });

            audit.totalSize += stats.size;

            // Check for optimization opportunities
            if (sizeKB > 100 && type === 'js') {
                audit.recommendations.push(
                    'Consider splitting ' + filePath + ' into smaller modules'
                );
            }

            if (sizeKB > 50 && type === 'css') {
                audit.recommendations.push(
                    'Consider critical CSS extraction for ' + filePath
                );
            }
        }

        // Analyze JavaScript files
        grunt.file.expand(distDir + '/js/**/*.js').forEach(function(file) {
            analyzeFile(file, 'js');
        });

        // Analyze CSS files
        grunt.file.expand(distDir + '/css/**/*.css').forEach(function(file) {
            analyzeFile(file, 'css');
        });

        // Output results
        grunt.log.writeln('Performance Audit Results:');
        grunt.log.writeln('========================');
        grunt.log.writeln('Total assets: ' + audit.assets.length);
        grunt.log.writeln('Total size: ' + (audit.totalSize / 1024).toFixed(2) + ' KB');
        grunt.log.writeln('');

        grunt.log.writeln('Asset Details:');
        audit.assets.forEach(function(asset) {
            grunt.log.writeln(
                '  ' + asset.type.toUpperCase() + ': ' +
                asset.path + ' (' + asset.sizeKB + ' KB)'
            );
        });

        if (audit.recommendations.length > 0) {
            grunt.log.writeln('');
            grunt.log.writeln('Recommendations:');
            audit.recommendations.forEach(function(rec) {
                grunt.log.writeln('  • ' + rec);
            });
        }
    });

    // Custom task: Dependency checker
    grunt.registerTask('deps-check', 'Check for outdated dependencies', function() {
        var shell = require('shelljs');
        var pkg = require('./package.json');

        grunt.log.writeln('Checking for outdated dependencies...');

        var outdated = shell.exec('npm outdated --json', { silent: true }).stdout;

        if (outdated) {
            var outdatedDeps = JSON.parse(outdated);
            var keys = Object.keys(outdatedDeps);

            if (keys.length > 0) {
                grunt.log.writeln('Outdated dependencies found:');
                keys.forEach(function(dep) {
                    var info = outdatedDeps[dep];
                    grunt.log.writeln(
                        '  ' + dep + ': current=' + info.current +
                        ', wanted=' + info.wanted + ', latest=' + info.latest
                    );
                });
            } else {
                grunt.log.writeln('All dependencies are up to date!');
            }
        }
    });

    // Default task
    grunt.registerTask('default', ['build:dev']);
};

💻 Développement de Plugins Grunt javascript

🟡 intermediate ⭐⭐⭐⭐

Création de plugins personnalisés Grunt et extension de la fonctionnalité Grunt

⏱️ 40 min 🏷️ grunt, plugins, development
Prerequisites: Grunt basics, JavaScript ES6+, Plugin architecture
// Grunt Plugin Development Guide
// Creating custom plugins and extending Grunt functionality

// ===== BASIC PLUGIN STRUCTURE =====

// 1. Simple File Processor Plugin
// grunt-plugin-file-processor.js
'use strict';

module.exports = function(grunt) {
    grunt.registerMultiTask('fileProcessor', 'Process files with custom logic', function() {
        var options = this.options({
            encoding: 'utf8',
            process: function(content) {
                return content; // Default: no processing
            },
            extension: '.processed'
        });

        this.files.forEach(function(file) {
            var dest = file.dest;
            var contents = file.src.map(function(filepath) {
                var content = grunt.file.read(filepath, { encoding: options.encoding });
                return options.process(content, filepath);
            }).join('\n');

            grunt.file.write(dest, contents);
            grunt.log.writeln('Processed file: ' + dest);
        });
    });
};

// 2. Multi-Task Plugin with Template Support
// grunt-plugin-template-processor.js
module.exports = function(grunt) {
    grunt.registerMultiTask('templateProcessor', 'Process templates with data', function() {
        var options = this.options({
            data: {},
            delimiters: ['{{', '}}'],
            ext: '.html'
        });

        // Load template data
        var templateData = {};
        if (typeof options.data === 'string') {
            templateData = grunt.file.readJSON(options.data);
        } else {
            templateData = options.data;
        }

        this.files.forEach(function(file) {
            var src = file.src[0];
            var dest = file.dest || src;

            if (grunt.file.exists(src)) {
                var content = grunt.file.read(src);
                var processed = grunt.template.process(content, {
                    data: templateData,
                    delimiters: options.delimiters
                });

                grunt.file.write(dest, processed);
                grunt.log.writeln('Processed template: ' + dest);
            }
        });
    });
};

// 3. Async Plugin with Progress Reporting
// grunt-plugin-async-processor.js
module.exports = function(grunt) {
    grunt.registerTask('asyncProcessor', 'Process files asynchronously', function() {
        var done = this.async();
        var options = this.options({
            concurrency: 5,
            timeout: 30000
        });

        var files = this.filesSrc;
        var processed = 0;
        var total = files.length;

        // Progress reporting
        var progressInterval = setInterval(function() {
            grunt.log.writeln('Progress: ' + processed + '/' + total + ' files processed');
        }, 1000);

        // Process files with limited concurrency
        var processFile = function(filepath, callback) {
            var startTime = Date.now();

            setTimeout(function() {
                // Simulate file processing
                grunt.file.read(filepath);
                processed++;

                var duration = Date.now() - startTime;
                grunt.log.writeln('Processed: ' + filepath + ' (' + duration + 'ms)');

                callback(null, filepath);
            }, Math.random() * 2000);
        };

        // Queue management
        var queue = [];
        var active = 0;

        function processQueue() {
            while (active < options.concurrency && queue.length > 0) {
                active++;
                var filepath = queue.shift();

                processFile(filepath, function() {
                    active--;
                    if (queue.length > 0) {
                        processQueue();
                    } else if (active === 0) {
                        clearInterval(progressInterval);
                        done();
                    }
                });
            }
        }

        // Add files to queue
        files.forEach(function(filepath) {
            queue.push(filepath);
        });

        // Start processing
        processQueue();

        // Timeout handling
        setTimeout(function() {
            if (processed < total) {
                clearInterval(progressInterval);
                grunt.log.error('Processing timed out');
                done(false);
            }
        }, options.timeout);
    });
};

// ===== ADVANCED PLUGIN EXAMPLES =====

// 4. CSS Sprite Generator Plugin
// grunt-plugin-css-sprite.js
module.exports = function(grunt) {
    var.spritesmith = require('spritesmith');
    var crypto = require('crypto');

    grunt.registerMultiTask('cssSprite', 'Generate CSS sprites from images', function() {
        var done = this.async();
        var options = this.options({
            algorithm: 'binary-tree',
            padding: 2,
            engine: 'pixelsmith',
            cssFormat: 'css',
            cssTemplate: null,
            cssVarMap: function(sprite) {
                return sprite.name;
            }
        });

        this.files.forEach(function(file) {
            var sprites = file.src.map(function(filepath) {
                return {
                    src: filepath,
                    name: grunt.config('process').basename(filepath, '.png').replace('@2x', '')
                };
            });

            spritesmith.create({
                src: file.src,
                dest: file.dest,
                engine: options.engine,
                algorithm: options.algorithm,
                padding: options.padding
            }, function(err, result) {
                if (err) {
                    grunt.log.error(err);
                    return done(err);
                }

                // Write sprite image
                grunt.file.write(file.dest, result.image);

                // Generate CSS
                var css = generateCSS(result.coordinates, result.properties, options);
                var cssPath = file.dest.replace(/\.png$/, '.' + options.cssFormat);

                grunt.file.write(cssPath, css);

                grunt.log.writeln('Generated sprite: ' + file.dest);
                grunt.log.writeln('Generated CSS: ' + cssPath);
                done();
            });
        });

        function generateCSS(coordinates, properties, options) {
            var css = '';
            var baseName = properties.name || 'sprite';

            for (var name in coordinates) {
                var sprite = coordinates[name];
                css += '.sprite-' + name + ' {\n';
                css += '  background-image: url("' + baseName + '.png");\n';
                css += '  background-position: ' + (-sprite.x) + 'px ' + (-sprite.y) + 'px;\n';
                css += '  width: ' + sprite.width + 'px;\n';
                css += '  height: ' + sprite.height + 'px;\n';
                css += '}\n\n';
            }

            return css;
        }
    });
};

// 5. Performance Optimizer Plugin
// grunt-plugin-perf-optimizer.js
module.exports = function(grunt) {
    var UglifyJS = require('uglify-js');
    var CleanCSS = require('clean-css');
    var imagemin = require('imagemin');
    var mozjpeg = require('imagemin-mozjpeg');
    var pngquant = require('imagemin-pngquant');

    grunt.registerMultiTask('perfOptimizer', 'Optimize files for performance', function() {
        var options = this.options({
            jsOptimization: true,
            cssOptimization: true,
            imageOptimization: true,
            report: true,
            threshold: 1024 * 1024 // 1MB
        });

        var stats = {
            files: 0,
            originalSize: 0,
            optimizedSize: 0,
            compressionRatio: 0
        };

        var done = this.async();

        this.files.forEach(function(file) {
            file.src.forEach(function(src) {
                if (grunt.file.isFile(src)) {
                    var extension = grunt.config('process').extname(src).toLowerCase();
                    var originalContent = grunt.file.read(src);
                    var optimizedContent = originalContent;
                    var originalSize = originalContent.length;

                    stats.files++;
                    stats.originalSize += originalSize;

                    // JavaScript optimization
                    if (extension === '.js' && options.jsOptimization) {
                        var result = UglifyJS.minify(originalContent);
                        if (!result.error) {
                            optimizedContent = result.code;
                            grunt.log.writeln('Optimized JavaScript: ' + src);
                        }
                    }

                    // CSS optimization
                    else if (extension === '.css' && options.cssOptimization) {
                        var cleanCSS = new CleanCSS();
                        var result = cleanCSS.minify(originalContent);
                        optimizedContent = result.styles;
                        grunt.log.writeln('Optimized CSS: ' + src);
                    }

                    // Image optimization
                    else if (['.png', '.jpg', '.jpeg', '.gif', '.svg'].includes(extension) &&
                             options.imageOptimization) {
                        imagemin([src], {
                            plugins: [
                                imagemin.gifsicle(),
                                imagemin.jpegtran(),
                                imagemin.optipng(),
                                imagemin.svgo(),
                                pngquant(),
                                mozjpeg()
                            ]
                        }).then(function(files) {
                            if (files && files[0]) {
                                grunt.file.write(file.dest || src, files[0].data);
                                var optimizedSize = files[0].data.length;
                                updateStats(originalSize, optimizedSize);
                            }
                        });
                        return;
                    }

                    // Write optimized content
                    grunt.file.write(file.dest || src, optimizedContent);
                    updateStats(originalSize, optimizedContent.length);
                }
            });
        });

        function updateStats(original, optimized) {
            stats.optimizedSize += optimized;
            stats.compressionRatio = ((stats.originalSize - stats.optimizedSize) / stats.originalSize * 100).toFixed(2);
        }

        // Generate report
        if (options.report) {
            setTimeout(function() {
                grunt.log.writeln('\nPerformance Optimization Report');
                grunt.log.writeln('================================');
                grunt.log.writeln('Files processed: ' + stats.files);
                grunt.log.writeln('Original size: ' + (stats.originalSize / 1024).toFixed(2) + ' KB');
                grunt.log.writeln('Optimized size: ' + (stats.optimizedSize / 1024).toFixed(2) + ' KB');
                grunt.log.writeln('Compression ratio: ' + stats.compressionRatio + '%');

                if (stats.originalSize > options.threshold) {
                    grunt.log.warn('Total size exceeds threshold of ' + (options.threshold / 1024) + ' KB');
                }

                done();
            }, 1000);
        } else {
            done();
        }
    });
};

// 6. i18n Bundle Generator Plugin
// grunt-plugin-i18n.js
module.exports = function(grunt) {
    grunt.registerMultiTask('i18nBundle', 'Generate i18n translation bundles', function() {
        var options = this.options({
            languages: ['en', 'es', 'fr'],
            defaultLanguage: 'en',
            outputFormat: 'json',
            namespace: 'translations'
        });

        var translations = {};
        var defaultTranslations = {};

        // Load translations from source files
        this.files.forEach(function(file) {
            file.src.forEach(function(src) {
                if (grunt.file.exists(src)) {
                    var content = grunt.file.readJSON(src);
                    var lang = grunt.config('process').basename(src, '.json');

                    translations[lang] = content;

                    if (lang === options.defaultLanguage) {
                        defaultTranslations = content;
                    }
                }
            });
        });

        // Generate bundles
        options.languages.forEach(function(lang) {
            var bundle = mergeTranslations(defaultTranslations, translations[lang]);
            var outputDir = file.dest || 'dist/i18n';
            var outputFile = outputDir + '/' + lang + '.json';

            grunt.file.write(outputFile, JSON.stringify(bundle, null, 2));
            grunt.log.writeln('Generated i18n bundle: ' + outputFile);
        });

        function mergeTranslations(defaults, translations) {
            if (!translations) {
                return defaults;
            }

            var merged = {};
            var keys = Object.keys(defaults);

            keys.forEach(function(key) {
                if (translations[key]) {
                    merged[key] = translations[key];
                } else {
                    merged[key] = defaults[key];
                }
            });

            return merged;
        }
    });
};

// ===== PLUGIN TESTING FRAMEWORK =====

// 7. Plugin Test Framework
// grunt-plugin-test.js
module.exports = function(grunt) {
    var path = require('path');

    grunt.registerTask('testPlugin', 'Test a Grunt plugin', function(pluginName, testFile) {
        if (!pluginName) {
            grunt.log.error('Plugin name is required');
            return;
        }

        var testPath = testFile || 'test/' + pluginName + '_test.js';

        if (!grunt.file.exists(testPath)) {
            grunt.log.error('Test file not found: ' + testPath);
            return;
        }

        // Load the plugin
        var pluginPath = path.resolve('node_modules/grunt-' + pluginName);

        try {
            require(pluginPath);
        } catch (e) {
            // Try local plugin path
            pluginPath = path.resolve('plugins/grunt-' + pluginName + '.js');
            require(pluginPath);
        }

        // Run tests
        try {
            require(testPath)(grunt);
            grunt.log.ok('Plugin tests passed: ' + pluginName);
        } catch (e) {
            grunt.log.error('Plugin tests failed: ' + pluginName);
            grunt.log.error(e.message);
        }
    });
};

// Example test file for fileProcessor plugin
// test/fileProcessor_test.js
module.exports = function(grunt) {
    var fs = require('fs');
    var tempDir = 'test/temp';

    // Setup test environment
    grunt.task.run('setup:test');

    // Test cases
    var tests = [
        {
            name: 'Basic file processing',
            input: 'test/fixtures/sample.txt',
            expected: 'test/expected/sample.processed.txt',
            options: {}
        },
        {
            name: 'Custom processing function',
            input: 'test/fixtures/sample.txt',
            expected: 'test/expected/sample.uppercase.txt',
            options: {
                process: function(content) {
                    return content.toUpperCase();
                }
            }
        }
    ];

    // Run tests
    tests.forEach(function(test) {
        grunt.task.options('fileProcessor', test.options);

        grunt.task.run('fileProcessor', {
            src: [test.input],
            dest: tempDir + '/processed.txt'
        });

        // Verify output
        var actual = fs.readFileSync(tempDir + '/processed.txt', 'utf8');
        var expected = fs.readFileSync(test.expected, 'utf8');

        if (actual === expected) {
            grunt.log.ok('✓ ' + test.name);
        } else {
            grunt.log.error('✗ ' + test.name);
            grunt.log.error('Expected: ' + expected);
            grunt.log.error('Actual: ' + actual);
        }
    });

    // Cleanup
    grunt.task.run('cleanup:test');
};

// ===== PLUGIN REGISTRATION =====

// Register all plugins
module.exports = function(grunt) {
    // Load individual plugins
    require('./grunt-plugin-file-processor')(grunt);
    require('./grunt-plugin-template-processor')(grunt);
    require('./grunt-plugin-async-processor')(grunt);
    require('./grunt-plugin-css-sprite')(grunt);
    require('./grunt-plugin-perf-optimizer')(grunt);
    require('./grunt-plugin-i18n')(grunt);
    require('./grunt-plugin-test')(grunt);

    // Plugin configuration
    grunt.initConfig({
        // File processor configuration
        fileProcessor: {
            basic: {
                files: {
                    'dist/processed.txt': ['src/input.txt']
                }
            },
            advanced: {
                options: {
                    process: function(content, filepath) {
                        return 'Processed from ' + filepath + ':\n' + content;
                    }
                },
                files: {
                    'dist/advanced.txt': ['src/**/*.txt']
                }
            }
        },

        // Template processor configuration
        templateProcessor: {
            development: {
                options: {
                    data: 'config/dev.json'
                },
                files: {
                    'dist/index.html': 'src/index.html'
                }
            }
        },

        // CSS sprite configuration
        cssSprite: {
            options: {
                algorithm: 'binary-tree',
                padding: 2,
                cssFormat: 'scss',
                cssVarMap: function(sprite) {
                    return 'sprite-' + sprite.name;
                }
            },
            all: {
                src: ['src/images/sprites/**/*.png'],
                dest: 'dist/images/sprite.png'
            }
        },

        // Performance optimizer configuration
        perfOptimizer: {
            options: {
                jsOptimization: true,
                cssOptimization: true,
                imageOptimization: true,
                report: true
            },
            assets: {
                files: {
                    'dist/': ['src/**/*.{js,css,png,jpg,jpeg,gif,svg}']
                }
            }
        },

        // i18n configuration
        i18nBundle: {
            options: {
                languages: ['en', 'es', 'fr', 'de', 'ja'],
                defaultLanguage: 'en'
            },
            translations: {
                src: ['src/i18n/*.json'],
                dest: 'dist/i18n'
            }
        }
    });

    // Register tasks
    grunt.registerTask('dev', ['fileProcessor', 'templateProcessor:development']);
    grunt.registerTask('optimize', ['perfOptimizer', 'cssSprite']);
    grunt.registerTask('i18n', ['i18nBundle']);
    grunt.registerTask('build', ['dev', 'optimize', 'i18n']);
    grunt.registerTask('default', ['build']);
};

// ===== PLUGIN DISTRIBUTION =====

// package.json for plugin distribution
{
  "name": "grunt-custom-plugins",
  "version": "1.0.0",
  "description": "Collection of custom Grunt plugins",
  "main": "index.js",
  "scripts": {
    "test": "grunt test",
    "lint": "grunt eslint",
    "build": "grunt build"
  },
  "keywords": ["gruntplugin", "automation", "build", "optimization"],
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "grunt": "^1.6.1",
    "spritesmith": "^4.0.0",
    "uglify-js": "^3.17.0",
    "clean-css": "^5.3.0",
    "imagemin": "^8.0.0",
    "imagemin-mozjpeg": "^10.0.0",
    "imagemin-pngquant": "^9.0.0"
  },
  "peerDependencies": {
    "grunt": ">=1.0.0"
  },
  "devDependencies": {
    "grunt-contrib-clean": "^2.0.0",
    "grunt-contrib-concat": "^2.1.0",
    "grunt-contrib-copy": "^1.0.0",
    "grunt-eslint": "^24.3.0"
  },
  "engines": {
    "node": ">=14.0.0"
  }
}

// Plugin documentation template
/*
# Grunt Custom Plugins

A collection of custom Grunt plugins for advanced build automation.

## Installation

```bash
npm install grunt-custom-plugins --save-dev
```

## Available Plugins

### fileProcessor

Process files with custom logic.

```javascript
grunt.initConfig({
    fileProcessor: {
        options: {
            process: function(content, filepath) {
                // Custom processing logic
                return processedContent;
            }
        },
        files: {
            'dist/processed.txt': ['src/input.txt']
        }
    }
});
```

### templateProcessor

Process templates with data.

```javascript
grunt.initConfig({
    templateProcessor: {
        options: {
            data: 'config.json',
            delimiters: ['{{', '}}']
        },
        files: {
            'dist/index.html': 'src/index.html'
        }
    }
});
```

### cssSprite

Generate CSS sprites from images.

```javascript
grunt.initConfig({
    cssSprite: {
        options: {
            algorithm: 'binary-tree',
            cssFormat: 'scss'
        },
        files: {
            'dist/sprite.png': ['src/sprites/*.png']
        }
    }
});
```

## Testing

```bash
npm test
```

## License

MIT
*/