WordPress Development Using NPM Scripts - studio4130
Over the years the way in which I have set up my workflow for WordPress development has changed. Initially, it was the way most folks did it. There were no ‘watch’ tasks. There was no Sass. Make a change. Switch to the browser and refresh. Over the years I have tried various setups. Many were painful, and not worth the effort. Then came Grunt, followed by Gulp. I jumped on the task runner bandwagon and developed many sites using Grunt, and a few using Gulp. Simultaneously, I was working on many Vue.js, React, and Angular projects, and became comfortable with Node and NPM. At one point I had back-to-back static HTML, CSS, and JavaScript application prototypes to build, and decided to skip Grunt and Gulp, and just use Node and NPM.
After using that workflow a few times, I decided to bring it to my WordPress development. It’s been my go-to setup ever since. The following is what I am currently using.
Requirements
This is a pretty simple setup, so there are not too many requirements. The trickiest is setting up a local LAMP or LEMP type environment. If you are currently developing WordPress sites, you have probably already figured this out. If not do some research online. Or check back here at some point, as I’ll write up my local WordPress development environment setup.
The second requirement is Node and NPM. If you install Node, you’ll get NPM. Once you’ve installed Node and NPM, you can move on to the steps to set up a NPM based workflow for WordPress development. If you prefer Yarn, you can substitute Yarn for NPM. I’m not going to go into the difference, or how to use Yarn, but it’s a pretty simple substitution.
NPM Setup
In your preferred Command Line environment navigate to your local project. I put my projects in c:/projects, and use the Window’s Subsystem for Linux, Ubuntu Bash Shell. From there, run npm init:
npm init
I’m calling my project ‘npm-wordpress’. Obviously, you’ll name it something different. So, where ever ‘npm-wordpress’ shows up, you can substitute the name of your project. I run ‘npm init’ in the root of my project, though many would prefer to run it in the theme instead. I run it from the root, as I want the script to be able to watch for any changes to any files, not just those in the theme. I also often package up the entire WordPress project using a build process also run by an NPM script. Since I want to package up everything, it needs to be in the root. Finally, I generally start projects by created a Git repository in Bitbucket, and the entire project resides there, not just the theme. When new developers join a project, they can clone once, run ‘npm install’ from the root, and get to work. I use a .gitignore file to ignore all of the ‘node_modules’ that we install with NPM to save space in the repository, and save time in cloning.
Running npm init
will bring up the following. You can fill out any, or all of the properties. If you’ve checked your project out of Git repository, it will automatically fill that value in. It will also fill in the package name based on the directory you’ve run the command in. In my case the directory for my project is ‘npm-wordpress’.
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (npm-wordpress)
version: (1.0.0)
description: WordPress NPM Development Setup
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
After hitting ‘Enter’ on the ‘License’, you’ll see the following:
About to write to /mnt/d/projects/NPM-WordPress/package.json:
{
"name": "npm-wordpress",
"version": "1.0.0",
"description": "WordPress NPM Development Setup",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Is this OK? (yes)
If you are satisfied, hit ‘Enter’ again, and you’ll be returned to the prompt. Now, if you look inside of the project directory, you should see a ‘package.json’ file. If you open it, it should look something like this:
{
"name": "npm-wordpress",
"version": "1.0.0",
"description": "WordPress NPM Development Setup",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Now, you can begin to add NPM Node Packages and Modules. You can add packages in the following manner:
npm install --save-dev <package name>
or
npm i -D <package name>
Where <package name> will be something like ‘autoprefixer‘. The second option, is shorthand for the first.
In this setup, I’ll use the following Node Packages:
Autoprefixer adds browser specific prefixes to CSS style properties. So, instead writing this in your CSS:
::-webkit-input-placeholder {
color: gray;
}
:-ms-input-placeholder {
color: gray;
}
::-ms-input-placeholder {
color: gray;
}
::placeholder {
color: gray;
}
You can do this:
::placeholder { color: gray; }
Postcss-cli is used in conjunction with Autoprefixer. It’s necessary to run Autoprefixer with NPM scripts.
Browsersync allows you to have the browser reload when you make file changes in the project, and in the case of CSS, it will simply inject the style changes into the browser without a full refresh of the page. Additionally, you can sync multiple browsers at once, and if you scroll one, the others will scroll too.
Node Sass will compile your .sass or .scss files into .css files. From the Github page:
“Node-sass is a library that provides binding for Node.js to LibSass, the C version of the popular stylesheet preprocessor, Sass.
It allows you to natively compile .scss files to css at incredible speed and automatically via a connect middleware.”
Npm-run-all allows for more concise NPM scripts, which makes our lives easier. For instance, in the package.json file, we’ll add scripts to run browsersync, node-sass, and other tasks. Typically that would require multiple ‘npm run’ statements, like:
npm run clean && npm run build:css && npm run build:js && npm run build:html
With npm-run-all, you can simplify to:
npm-run-all clean build:*
Now that we’ve created the package.json file, and know what packages we want, the way to add the packages to the project would be to run:
npm i -D autoprefixer browser-sync node-sass npm-run-all postcss-cli
If you prefer, you can add each one individually:
npm i -D autoprefixer
npm i -D browser-sync
npm i -D node-sass
npm i -D npm-run-all
npm i -D postcss-cli
At this point the package.json file will look like this:
{
"name": "npm-wordpress",
"version": "1.0.0",
"description": "WordPress NPM Development Setup",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^9.4.3",
"browser-sync": "^2.26.3",
"node-sass": "^4.11.0",
"npm-run-all": "^4.1.5",
"postcss-cli": "^6.1.0"
}
}
The versions may differ, of course, but the packages we installed with the ‘-D’ flag have been written to package.json as dev dependencies.
NPM Scripts
At this point we can add NPM scripts. We’ll want a script to start a server and proxy our local WordPress server. For instance, I run my local WordPress projects using Apache VirtualHosts. So, I may have my project running at: http://local.myproject.com. Browsersync will start a server accessible at http://localhost:3000, and proxy the http://local.myproject.com. This allows the project to use Apache, MySql, and PHP, as required by WordPress, but also reload the page whenever a file changes. This script, while simple in appearance, took some time to figure out. The docs on the Browsersync site are a little slim when it comes to Command Line usage.
We’ll also want a script to compile Sass into Css, followed by the watch command, so that when subsequent changes are made to Sass files, the CSS will be recompiled. And, finally, we want to use Autoprefixer to add browser prefixes to the compiled CSS file.
The NPM scripts look like:
{
"sass-build": "node-sass wp-content/themes/npm-wordpress/scss -o wp-content/themes/npm-wordpress/css",
"sass-watch": "node-sass wp-content/themes/npm-wordpress/scss -o wp-content/themes/npm-wordpress/css -w",
"prefix-build": "postcss wp-content/themes/npm-wordpress/css/style.css -u autoprefixer -o wp-content/themes/npm-wordpress/style.css -w",
"browser-start": "browser-sync 'http://local.npm-wordpress.com' -f='wp-content/themes/*/**.*' -w",
"dev": "npm-run-all sass-build --parallel sass-watch browser-start prefix-build"
}
The ‘dev’ script starts everything off. npm run dev
will run the ‘dev’ script. The script uses ‘npm-run-all’ to run other scripts, including ‘sass-build’, followed by ‘sass-watch’, ‘browser-start’, and ‘prefix-build’. the last three run in parallel with the –parallel flag. This means they don’t have to wait for previous script to run first.
‘sass-build’ uses ‘node-sass’ to take .scss files in ‘wp-content/themes/npm-wordpress/scss’ and outputs the compiled CSS file to ‘wp-content/themes/npm-wordpress/css’. The output directory is temporary. It’s necessary for the ‘prefix-build’ script. We want to run the ‘sass-build’ at start so that we have an initial compiled CSS file. Without we’d have to make a change to an .scss file to kick of the CSS compilation. I also generally use .gitignore to ignore the final compiled ‘style.css’ file as it will almost always have a conflict when multiple developers work on a project. A build script would include the compiled ‘style.css’ file for deployment.
Next, ‘sass-watch’ is run, which is essentially the same as the build, but with a ‘watch’ flag, which is the ‘-w’ at the end of the script. The watch means that anytime .scss files change, the files will be compiled and output into the same output director as the ‘sass-build’ script: ‘wp-content/themes/npm-wordpress/css’. Again, this is so the ‘prefix-build’ script can run on the compiled CSS file(s).
The ‘prefix-build’ script which was run in parallel to the ‘sass-watch’ and ‘browser-start’ scripts, takes the compiled CSS, adds browser prefixes, and outputs the final ‘style.css’ file in the theme root, as is standard for WordPress themes.
Also run at the same time is the ‘browser-start’ script with uses Browsersync to start up a server at ‘http://localhost:3000’, proxy the URL specified after ‘browser-sync’, in this case ‘http://local.npm-wordpress.com’, open a browser, watch files in ‘wp-content/themes/*/**.*’, and reload the browser when those files change. The watched files could be the entire project, as mentioned previously, or just theme files as demonstrated here.
So, the final package.json file should look something like this:
{
"name": "npm-wordpress",
"version": "1.0.0",
"description": "\"WordPress NPM Development Setup\"",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"sass-build": "node-sass wp-content/themes/npm-wordpress/scss -o wp-content/themes/npm-wordpress/css",
"sass-watch": "node-sass wp-content/themes/npm-wordpress/scss -o wp-content/themes/npm-wordpress/css -w",
"prefix-build": "postcss wp-content/themes/npm-wordpress/css/style.css -u autoprefixer -o wp-content/themes/npm-wordpress/style.css -w",
"browser-start": "browser-sync 'http://local.npm-wordpress' -f='wp-content/themes/*/**.*' -w",
"dev": "npm-run-all sass-build --parallel sass-watch browser-start prefix-build"
},
"repository": {
"type": "git",
"url": "git+ssh://[email protected]/kbauman/npm-wordpress.git"
},
"author": "",
"license": "ISC",
"homepage": "https://bitbucket.org/kbauman/npm-wordpress#readme",
"devDependencies": {
"autoprefixer": "^9.4.3",
"browser-sync": "^2.26.3",
"node-sass": "^4.11.0",
"npm-run-all": "^4.1.5",
"postcss-cli": "^6.1.0"
}
}
Again, the version numbers will likely be different, but the overall contents of the package.json file should look a lot alike.
At this point, from the root, running npm run dev
should run without errors and you should see something like this:
Your browser should also open a new tab and load your WordPress site at http://localhost:3000. Any changes to any files, other than Sass or Css, in the theme directory should cause a browser refresh. Sass and CSS changes should be injected straight into the page without a complete refresh.
Since I wrote this I’ve set up a new laptop with Windows 10, using Ubuntu 18.04 on the Windows Subsystem for Linux, and found that the Browsersync proxy didn’t work. It turns out that I needed to edit the ‘hosts’ – /etc/hosts – file on Ubuntu, and add 127.0.0.1 local.npm-wordpress.com
Leave a comment