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.
Be First to Comment