Skip to content

WordPress Gruntfile Template

Gruntfile.js for WordPress plugins

After writing out a Gruntfile.js for the last several projects I finally dragged myself into creating a template instead of copying and pasting every time.

This one is specific to WordPress plugins, but only because it includes the wp-i18n tasks. They take care of making sure that all i18n functions have the correct language domain and building the pot files. Next non WordPress project I build I'm probably going to start with this file and make a new template without the wp-i18n stuff.

What's there?

concat: Takes multiple JavaScript files and concatenates it all in to one file. For this template it's just pulling every JavaScript file out of the js/src folder and concatenating into the js/dist folder. But you can split into different files if needed.

uglify: Once the JavaScript is concatenated, this takes it and minifies into a file in js/dist/ with the same name, but a .min.js extension.

sass: Runs SCSS files through SASS.

wp-18n: Builds a POT file from the PHP files to make the plugin easier to translate. It also will add the language domain to any i18n methods, but that has to be run manually.

watch: Watches a bunch of files and fires off tasks as they change so I don't have to remember.

Settings

Everything is in the opts property.

jsName: Name of the JavaScript files created. Defaults to project which would create the files project.js and project.min.js in the js/dist folder.

cssName: Name of the CSS file created in css/dist, and also the name of the parent SCSS file in css/src/.

companyName, companySite: These are used in the copyright headers of the concatenated JavaScript files.

langDomain: Language Domain used for the i18n WordPress functions.

bugsURL: Address listed in the POT files created for issues

Assumptions

The Gruntfile makes a couple of assumptions about how the project is laid out. It's what I like, but could be changed pretty easily.

JavaScript files live in js/src and they're all concatenated to js/dist/project.js and js/dist/project.min.js. You can change the filenames under opts.

SCSS files live in css/src and are combined into css/dist/style.css. Same thing, you can change the filename in opts.

package.json

There are a couple of requirements needed in your package.json file. I'm also leaving a template for that. It's just the dependencies for grunt.

Once you've put the Gruntfile.js and package.json files in your project run npm i at console to take care of loading all the dependencies.

The package versions aren't critical. They just happen to be what was current when I installed them.

Gruntfile.js

'use strict';
module.exports = function (grunt) {
  grunt.initConfig({
    opts: {
      /* Filename of the compiled JavaScript file */
      jsName: 'project',

      /* Filename of the compiled CSS file */
      cssName: 'style',
      companyName: 'Company Name',
      companySite: 'https://www.example.com',
      langDomain: 'language-domain',
      bugsURL: 'https://www.example.com/issues-or-forum',

      /* Name of the plugin boot file. This is the one with the plugin header for WordPress. */
      pluginFile: 'plugin.php'
    },
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      options: {
        separator: ';\r\n'
      },
      dist: {
        src: [
          'js/src/*.js',
          'js/src/**/*.js'
        ],
        dest: 'js/dist/<%= opts.jsName %>.js'
      }
    },
    uglify: {
      options: {
        banner: '/* (c) <%= grunt.template.today("yyyy") %> <%= opts.companyName %> - <%= opts.companySite %> */\n',
        report: 'min',
        compress: {
          drop_console: false,
          sequences: false
        }
      },
      dist: {
        files: {
          'js/dist/<%= opts.jsName %>.min.js': ['<%= concat.dist.dest %>']
        }
      }
    },
    watch: {
      js: {
        files: ['<%= concat.dist.src %>'],
        tasks: ['concat', 'uglify', 'notify:done']
      },
      css: {
        files: ['**/*.scss', '!node_modules/**', '!vendor/**'],
        tasks: ['sass', 'notify:css']
      },
      php: {
        files: ['*.php', '**/*.php', '!node_modules/**'],
        tasks: ['makepot', 'notify:php']
      }
    },
    notify: {
      done: {
        options: {
          title: '"JavaScript"',
          message: '"JavaScript code combined and uglified"'
        }
      },
      php: {
        options: {
          title: '"PHP tasks complete"',
          message: '"All PHP tasks complete"'
        }
      },
      css: {
        options: {
          title: '"SCSS tasks complete"',
          message: '"SCSS files compressed"'
        }
      }
    },
    makepot: {
      target: {
        options: {
          cwd: '',
          domainPath: 'lang',
          exclude: [],
          include: [],
          mainFile: '',
          potComments: '',
          potFilename: '',
          potHeaders: {
            poedit: true,
            'x-poedit-keywordslist': true,
            'Report-Msgid-Bugs-To': '<%= opts.bugsURL %>'
          },
          processPot: null,
          type: 'wp-plugin',
          updateTimestamp: true,
          updatePoFiles: false
        }
      }
    },
    addtextdomain: {
      options: {
        textdomain: '<%= opts.langDomain %>',
        updateDomains: true
      },
      target: {
        files: {
          src: [
            '*.php',
            '**/*.php',
            '!node_modules/**',
            '!tests/**'
          ]
        }
      }
    },
    sass: {
      all: {
        options: {
          style: 'compressed'
        },
        files: {
          'css/dist/<%= opts.cssName %>.min.css': 'css/src/<%= opts.cssName %>.scss'
        }
      }
    },
    bump: {
      options: {
        files: ['package.json'],
        updateConfigs: [],
        commit: false,
        commitMessage: 'Release v%VERSION%',
        commitFiles: ['package.json'],
        createTag: false,
        tagName: 'v%VERSION%',
        tagMessage: 'Version %VERSION%',
        push: false,
        pushTo: 'upstream',
        gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d',
        globalReplace: false,
        prereleaseName: false,
        metadata: '',
        regExp: false
      }
    },
    replace: {
      plugin_php: {
        src: ['<%= opts.pluginFile %>'],
        overwrite: true,
        replacements: [{
          from: /Version:\s*(.*)/,
          to: "Version:           <%= pkg.version %>"
        }]
      }
    }
  });
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-notify');
  grunt.loadNpmTasks('grunt-wp-i18n');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-bump');
  grunt.loadNpmTasks('grunt-text-replace');
  grunt.registerTask('default', ['concat', 'uglify', 'watch', 'notify:done']);
  grunt.registerTask('watchJS', ['watch']);
  grunt.registerTask('lang', ['addtextdomain', 'makepot']);

  grunt.registerTask('version', 'Updates version for the plugin', function (step) {
    if (step === undefined) {
      step = 'patch';
    }
    if (!['patch', 'minor', 'major'].includes(step)) {
      grunt.log.writeln('Invalid step for version upgrade:' + step);
      grunt.log.writeln('\n');
      grunt.log.writeln('grunt version - updates patch version\ngrunt version:patch - updates patch version\ngrunt version:minor - updates to next minor version\ngrunt version:major - updates to next major version\n\n');
      return;
    }
    grunt.task.run(['lang', 'bump:' + step, 'readpkg', 'replace:plugin_php']);
  });

  grunt.registerTask('readpkg', 'Read in the package.json file', function () {
    grunt.config.set('pkg', grunt.file.readJSON('./package.json'));
  });
};

package.json

{
  "name": "project-name",
  "version": "0.0.1",
  "description": "The coolest project ever!",
  "main": "Gruntfile.js",
  "scripts": {
    "test": "echo \"Error:no test specified\" && exit 1"
  },
  "author": "Your Name",
  "license": "GPL-3.0-or-later",
  "dependencies": {
    "grunt-bump": "^0.8.0",
    "grunt-contrib-concat": "^1.0.1",
    "grunt-contrib-sass": "^1.0.0",
    "grunt-contrib-uglify": "^4.0.1",
    "grunt-contrib-watch": "^1.1.0",
    "grunt-notify": "^0.4.5",
    "grunt-text-replace": "^0.4.0",
    "grunt-wp-i18n": "^1.0.3"
  }
}

If you’re interested, I’ve saved this as a gist as well.

Published inCodingWordPress

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *