Grunt 构建工具

Grunt JavaScript任务运行器示例,包括自动化、构建流程和部署工作流

💻 Grunt 基础任务 javascript

🟢 simple ⭐⭐

现代Web开发的基本Grunt任务,包括文件处理、监视和构建

⏱️ 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);
        }
    });
};

💻 Grunt 高级工作流 javascript

🟡 intermediate ⭐⭐⭐⭐

高级Grunt工作流,包括多目标构建、优化和部署策略

⏱️ 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') ? ';' : ';
',
                process: function(src, filepath) {
                    return '// Source: ' + filepath + '
' + 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') + ' */
',
                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']);
};

💻 Grunt 插件开发 javascript

🟡 intermediate ⭐⭐⭐⭐

创建自定义Grunt插件和扩展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
*/