This WordPress plugin demonstrates how to setup a plugin that uses VueJS and ES6 in a WordPress plugin (Frontend Widget, WordPress backend menu page) - within a fully customizable Docker development environment.
๐ ๐ฅ Instant no-config plugin creation with create-wp-react-app
Client-side features: Familiar VueJS API & patterns (ES6) with TypeScript
- VueJS v16 with Babel v7
env
preset + hooks - VUEX Centralized State Management for Vue.js.
- webpack v4 build for assets
- CSS and TypeScript Sourcemap generation for debugging purposes
- SASS stylesheets compiler (
.scss
files) - PostCSS for transforming SCSS (including autoprefixing) to CSS
- Generation of minified sources for production (JS, CSS)
- Grunt for automation tasks
- TSLint predefined configuration for proper linting
- Automatic generation of
.pot
files for translating (i18n) the frontend plugin - Admin backend components, in this case an own page with a button (
public/src/admin.js
) - Frontend components, in this case a simple widget (
public/src/widget.js
)
Server-side features: OOP-style for building a high-quality plugin.
- PHP >= 5.3 required: An admin notice is showed when not available
- WordPress >= 4.4 required: An admin notice is showed when not available with a link to the updater
- Namespace support
- Autloading classes in connection with namespaces
- WP REST API v2 for API programming, no longer use
admin-ajax.php
for your CRUD operations SCRIPT_DEBUG
enables not-minified sources for debug sources (use in connection withyarn build-dev
)- Cachebuster for public resources (
public
) - Automatic generation of
.pot
files for translating (i18n) the backend plugin - phpDocumentor for PHP Documentation
- TypeDoc for JavaScript Documentation
- apiDoc for API Documentation
- WP HookDoc for Filters & Actions Documentation
Other features: Providing the right development environment for a quality plugin.
- Built on top of VSCode (extensions are automatically installed)
- Prettier for automatic code beautifying on save (VSCode IDE)
- Prettier PHP for PHP beautifying (needs manual installation)
- Husky integration for code beautifying (PHP, TS) before GIT commit - never have ugly code in your repository
- Husky is also used for commitlint to become a common commit message style in your repository
- webpackbar so you can get a real progress bar while development
- Predefined GitLab CI example for Continous Integration, read more here
- Predefined Review Apps example for branch deployment, read more here
- Docker for a local development environment
- Within the Docker environment you have WP-CLI available
- Cypress for End-To-End (E2E) tests
- semantic-release for semantic versioning and changelog generation
- Node.js as JavaScript engine
- Yarn
yarn
command globally available in CLI (alternative to Node'snpm
) - WP-CLI
wp
command globally available in CLI - Composer
composer
command globally available in CLI - Docker
docker
anddocker-compose
command globally available in CLI
The brandnew create-wp-react-app allows you now to create your plugin with a single command!
$ yarn add -g create-wp-react-app
$ create-wp-react-app create my-plugin
Legacy Note: Since this starter plugin relies on a complete local environment with Docker, TypeScript and so on you can checkout the legacy-wo-docker
branch without such technologies. You can use create-wp-react-app create my-plugin --checkout legacy-wo-docker
to generate your plugin without Docker and TypeScript.
- Folder structure
- Available commands
- Local environment
- Make the boilerplate yours
- Available constants
- Activation hooks
- Add hooks and own classes
- Add external PHP library
- Add external JavaScript library
- Using the cachebuster
- WP REST API v2
- JavaScript state management
- Localization
- Building production plugin
- Using CI CD
assets
: Plugin assets for wordpress.orgbuild
: Build relevant files and predefined grunt tasksdocker
: Docker relevant files like containers, scripts and compose filesdist
: The production plugin, see Building production plugindocs
: Auto generated docs (for example for PHP, JS and API Doc), see Available commandsinc
: All server-side files (PHP)base
: Abstract base classesgeneral
: General files for the pluginmenu
: Example page (backend)others
: Other files (cachebusters, ...)rest
: Example REST API service, see WP REST API v2widget
: Example widget
languages
: Language files for backend coding (PHP)public
: All client-side files (TypeScript, SCSS)lib
: Put external libraries to this folder (cachebuster is only available for copied node modules, see Add external JavaScript library)src
: Your source files (see client-side features what's possible)languages
: Language files for frontend coding (PHP)dev
: Generated development sources (SCRIPT_DEBUG
is active)dist
: Generated production sources (SCRIPT_DEBUG
is not active)
index.php
: The plugins index file. The entry point for your plugin.uninstall.php
: When uninstalling your plugin this file gets called.babelrc
: Babel configuration.gitlab-ci.yml
: GitLab CI configuration.prettierrc
: Prettier configurationCHANGELOG.md
: Changelog file, do not change it because this is automatically done bysemantic-release
commitlint.config.js
: Commitlint configurationcomposer.json
: Composer package configurationGruntfile.js
: Grunt automation filepackage.json
: NPM package configurationpostcss.config.js
: PostCSS configurationREADME.md
: README file for your Git repositoryREADME.wporg.txt
: WordPress.org README filetsconfig.json
: TypeScript configurationtslint.json
: TSLint configurationwebpack.config.js
: webpack configuration
Commandย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | Context | Description |
---|---|---|
yarn start-development |
Docker, .tsx |
Starts to watch the public/src folder for file changes and automatically runs the build-dev script. Additionally the npm script webpack-build-done is executed after each webpack build. Before the watcher is started the Docker container gets created and started, see also Local environment |
yarn stop-development |
Docker | Stops the docker services |
yarn rm-development |
Docker | Removes the docker services. This does not remove any volumes so if you start the development again all is as before (installed plugins, uploaded files, ...) |
yarn purge-development |
Docker | Removes and purges the docker services compoletely with volumes included |
yarn docker-compose-name-wordpress |
Docker | Print out the dynamically generated container name of the wordpress service |
yarn wp-cli <command> |
Docker, WP-CLI | Run a WP-CLI command within the WordPress environment, for example yarn wp-cli "wp core version" . Use --silent to suppress the output of yarn itself |
yarn i18n-backend |
Localization, .php |
Generate .pot files from extracted localization strings of your backend coding |
yarn i18n-frontend |
Localization, .tsx |
Generate .pot files from extracted localization strings of your frontend coding |
yarn wp-wait |
WordPress, Docker | Waits and continues with process when the WordPress instance is ready |
yarn db-snapshot <file> |
Docker, WP-CLI, DB | Make a snapshot of the current defined database tables (see below how to configure) and safe to a file |
yarn db-snapshot-import-on-startup |
Docker, WP-CLI, DB | Make a snapshot of the current defined database tables (see below how to configure) and save them in that way, that the next WordPress install will import that snapshot |
yarn db-snapshot-import |
Docker, WP-CLI, DB | The installation snapshot taken with yarn db-snapshot-import-on-startup is imported to the current running Docker instance. This can be useful for tests for example |
yarn serve |
Source files | Bundles all the plugin files together and puts it into the dist folder. This folder can be pushed to the wordpress.org SVN. See Building production plugin. |
yarn build |
.tsx |
Create production build of ReactJS files. The files gets generated in public/dist . This files should be loaded when SCRIPT_DEBUG is not active. Learn more here: Building production plugin |
yarn build-dev |
.tsx |
Create development build of ReactJS files. The files gets generated in public/dev . This files should be loaded when SCRIPT_DEBUG is active. |
yarn lint |
.tsx |
Prints out errors and warning about coding styles. |
yarn phpdocs |
.php |
Generate PHP docs in docs/php of inc . |
yarn jsdocs |
.tsx |
Generate JS docs in docs/js of public/src . |
yarn apidocs |
.php |
Generate API docs in docs/api of inc . |
yarn hookdocs |
.php |
Generate Actions and Filters docs in docs/hooks of inc . |
yarn docs |
Source files | Generates all docs at once. |
yarn test |
Tests | Run all available tests (currently only e2e tests are available) |
yarn test-e2e |
Tests, Cypress | Runs the test files in cypress/integration in your current running localhost |
yarn cypress open |
Tests, Cypress | Opens the Cypress Test Runner as GUI |
yarn prettier-write |
Source files | Iterate all available source files (.tsx, .php) and pretty print them. |
yarn docker-ci-image-build |
Docker | Builds docker/container/ci/Dockerfile so it can be pushed. Usually you do not need this |
yarn docker-ci-image-push |
Docker | Pushes the built docker/container/ci/Dockerfile to the docker-hub-username /docker-hub-image-name from package.json . Usually you do not need this |
yarn grunt public-cachebuster |
Libraries | Starts to generate the cachebuster files inc/others/cachebuster.php (including public/dist and public/dev hashes) and inc/others/cachebuster-lib.php (including public/lib ). Note: Each build with webpack triggers a cachebuster generation. |
yarn grunt copy-npmLibs |
Libraries | Copies the defined public libraries in Gruntfile.js to the public/lib folder. See Add external JavaScript library. |
When running yarn start-development
a local development environment with Docker Compose is started and you can start development with TypeScript and your PHP files. If you have a look at docker/development/docker-compose.yml
you can see that services are registered with the following exposed ports:
Host / Port | Description |
---|---|
localhost(:80) |
The webserver with WordPress installation itself |
localhost:3306 |
The MySQL database server |
localhost:8079 |
The phpMyAdmin interface |
Running the above command the docker/scripts/container-wordpress-command.sh
file is called. There you can define actions which should be executed to prepare your local environment so you have to for example not activate your plugin manually. If you want to initialize specific database tables after WordPress installation you can do the following steps, in this scenario we create a new blog post:
yarn start-development
so WordPress is installed inlocalhost
- Login to your WordPress instance and create a new post
- Define the tables which you want to snapshot for the startup in
package.json#db-snapshot
:"db-snapshot": ["wp_posts", "wp_postmeta"]
yarn db-snapshot-import-on-startup
to export the defined database tables into a file indocker/scripts/startup.sql
yarn purge-development
removes your current WordPress installation completelyyarn start-development
again and you will see that post is immediatly available after creation
Make it yours?! Sounds crazy. Yes, it means it can automatically change the constant names (PHP), namespace prefix (PHP) and the language .pot
filename. All the magic is done by the module create-wp-react-app. It will ask you a few plugin details in the CLI prompt and fully automatically generates the files for you. When the generator is finished just have a look at the index.php
file and all is setup for you.
After generating your boilerplate you should have a look in the generated index.php
file. There are several PHP constants available for your plugin coding:
YOURCONSTANTPREFIX_FILE
: The plugin file (__FILE__
)YOURCONSTANTPREFIX_PATH
: The plugins pathYOURCONSTANTPREFIX_INC
: The pluginsinc
folder with trailing slashYOURCONSTANTPREFIX_MIN_PHP
: The minimum PHP versionYOURCONSTANTPREFIX_NS
: The namespace for your pluginYOURCONSTANTPREFIX_DB_PREFIX
: TheBase.class.php
offers a methodgetTableName()
which returns a valid table name for your pluginYOURCONSTANTPREFIX_OPT_PREFIX
: If you want to save options you should use this constant for option names prefixYOURCONSTANTPREFIX_TD
: The text domain for your plugin. See LocalizationYOURCONSTANTPREFIX_VERSION
: The version of the pluginYOURCONSTANTPREFIX_DEBUG
: If true theBase.class.php::debug()
method writes to the error log
There are four types of activation hooks:
- Activate: This hook / code gets executed even the plugin gets activated in the WordPress backend. You can implement your code in
inc/general/Activator.class.php::activate()
. - Deactivate: This hook / code gets executed even the plugin gets deactivated in the WordPress backend. You can implement your code in
inc/general/Activator.class.php::deactivate()
. - Install: This hook / code gets executed when the plugin versions changes. That means every update of the plugin executes this code - also the initial plugin activation. Usually you should implement your database table creation with
dbDelta
here. You can implement your code ininc/general/Activator.class.php::install()
. - Uninstall: This hook / code gets executed even the plugin gets uninstalled in the WordPress backend. You can implement your code in
uninstall.php
.
Your action and filters can be registered in inc/general/Core.class.php::init()/__construct()
.
If you want to create your own classes / interfaces / enums, ... please create them in inc
with Classname.class.php
or IMyInterface.interface.php
. The inc
folder hiearchy represents the namespace prefix. For example you create a class in inc/my/package/Class.class.php
and your generated namespace prefix is Company\Plugin
the full name for the class should be Company\Plugin\my\package\Class
.
PHP libraries should be installed via Composer. If you want to use a PHP library in the plugin's production build (dist
) then install the dependency as non-dev dependency. The dist
build does not contain any dev dependencies.
When the composer dependency supports autoloading you do not have to worry about including. The boilerplate already includes the vendor autoload if exists.
When using external libraries or React components it is recommended to avoid bundling it with the plugin's source code (webpack). Because the WordPress community offers a lot of plugins you should enqueue the library files using the provided base\AssetsBase
(which is based on wp_enqueue_script
/wp_enqueue_style
) to avoid duplicate JavaScript assets.
In this example we want to use this NPM package in our WordPress plugin: https://www.npmjs.com/package/jquery-tooltipster. It is a simple tooltip plugin for jQuery.
- Run
yarn add install jquery-tooltipster --dev
to install the npm module. - Add the library name to the
Gruntfile.js
so it looks like this:
clean: {
/**
* Task to clean the already copied node modules to the public library folder
*/
npmLibs: [
'public/lib/jquery-tooltipster/'
]
},
copy: {
/**
* Task to copy npm modules to the public library folder.
*/
npmLibs: {
expand: true,
cwd: 'node_modules',
src: [
'jquery-tooltipster/js/*.js',
'jquery-tooltipster/css/*.css'
],
dest: 'public/lib/'
}
}
Note: The src
for your npm module can be different. You must have a look at the modules' folder tree.
- Run the command
yarn grunt copy-npmLibs
to copy the library and generate the new cachebuster for the library files. - Go to
Assets.class.php
and enqueue the styles and scripts:
// This must be before your VueJS styles and scripts so it can be used in VueJS
$this->enqueueLibraryScript('jquery-tooltipster', 'jquery-tooltipster/js/tooltipster.js');
$this->enqueueLibraryStyle('jquery-tooltipster', 'jquery-tooltipster/css/tooltipster.css');
// Add the dependencies to the VueJS styles and scripts
$this->enqueueScript('wp-vuejs-starter', 'admin.js', array('vue', 'jquery-tooltipster'));
$this->enqueueStyle('wp-vuejs-starter', 'admin.css', array('jquery-tooltipster');
- If you have a look at your browser network log you see that the plugin automatically appends the right module version to the resource URL.
- If you want to use the library in your VueJS coding simply add jQuery to the
webpack.config.js
file as external:
externals: {
'jquery': 'jQuery'
},
- And this in your VueJS file:
import $ from "jquery";
- Now you can use the
$.fn.tooltipster
functionality.
The class AssetsBase
(inc/general/AssetsBase.class.php
) provides a few scenarios of cachebusting enqueue (scripts and styles):
- Scenario 1 (NPM library): Add a dependency to
package.json
> Copy topublic/lib/{PACKAGE_NAME}
(using Grunt) > UseAssetsBase::enqueueLibraryScript()
to enqueue the handlepublic/lib/{PACKAGE_NAME}/{FILE}.js
for example. The cachebuster is applied with the node module version. See Add external JavaScript library for more. - Scenario 2 (Dist and Dev): While developing the
public/src
is automatically transformed to production / dev code. UseAssetsBase::enqueueScript()
to enqueue the handlepublic/dev/admin.js
for example. The cachebuster is applied with a hash. - Scenario 3 (Unknown library): Imagine you want to use a JavaScript library which is not installable through npm. > Use
AssetsBase::enqueLibraryScript()
(or wp_enqueue_script) to enqueue the handlepublic/lib/myprivatelib/file.js
for example. The cachebuster is applied with the plugin version.
The boilerplate needs a minimum WordPress version of 4.4. The WP REST API v2 is implemented in WordPress core in 4.7. The user gets an admin notice if < 4.4 to update WordPress core. If the user is > 4.4 and < 4.7 there is an admin notice with a link to download the WP REST API plugin. The boilerplate adds a localization key to provide the REST API url to JavaScript:
<Notice type="info">The WP REST API URL of the plugin is: "{window.wprjssOpts.restUrl}" (localized variable)</Notice>
Note: Using the WP REST API v2 is essential for high quality plugins, please avoid using admin-ajax.php
.
Never adjust your package.json
/index.php
version
manually!
Building a production plugin is completely covered by the GitLab CI. That means if you push changes to the master
branch the pipeline automatically generates the installable .zip
file. That file can be downloaded through GitLab Releases. The versioning is done via semantic-release
.
- Configure your GitLab CI to allow
push
, see also here - Configure a Git commiter when pushing to a protected branch, see here
Simply run yarn serve
and a folder dist
gets created with a subfolder of the installable plugin and an installable zip file. It is recommenend to use CI / CD to publish the new version to wordpress.org or other marketplaces. An instroduction how to do this can be read below.
This boilerplate plugin is built on top of GitLab CI and an own runner. The .gitlab-ci.yml
is the entrypoint for the pipeline configuration. If you want still use your own GIT repository you navigate to "New project" and "CI/CD for external repo". However, you should make sure that your GIT repository has two branches:
master
: If you merge something into master the CI/CD automatically pushes the changes to wordpress.org if you have activated that (see below). Also consider to "protect" this branch, this can be for example done in GitLabSettings > Repository > Protected Branches
develop
: Your development branch. All commits will be linted and tested and can only be merged tomaster
when all is successful. Here you should do your developments
Also you have to use your own GitLab CI Runner (I think it works also with shared runners, but I have not yet tested this). Here is a best practice how to do this:
- If you do not have yet your own server navigate to https://scaleway.com and order one (the cheapest Linux server should be enough)
- SSH into your server
- Install the
gitlab-runner
(see documentation here) - Register the GitLab runner onto your GitLab repository (see documentation here)
- Navigate to your repository
Settings > CI / CD
and deactivate the shared runners - You have to adjust some configurations within the GitLab Runner, so open the configuration file (see here) and diff it with the file
build/gitlab-runner-config.txt
in this repository. The main differences areconcurrent
,check_interval
,cache_dir
andvolumes
, so please do not completely replace this file. gitlab-runner restart
and finish!- Run also this container for garbage collection: https://gitlab.com/gitlab-org/gitlab-runner-docker-cleanup
In this section it is explained how to release a new plugin to wordpress.org. For example this plugin wp-reactjs-starter is built on top of this boilerplate. Generelly the initial release needs to be reviewed by the wordpress.org team so you have to prepare the installable plugin as zip file locally. Later - when upading the plugin - the GitLab CI is used.
- Add functionality to your plugin
- Adjust
CHANGELOG.md
andREADME.wporg.txt
files (you can use a README validator) - Prepare you images (header, icon, screenshots) in
assets
folder - When you think it is ready to release, run
yarn serve
- Navigate to
dist
and you will se a generated zip file - Upload that zip file to https://wordpress.org/plugins/developers/add/ for review
- Wait for approval
When the above initial review got approved you can go on with deployment via CI/CD:
- Navigate in your repository to
Settings > CI / CD > Variables
- Add the variable
WPORG_SVN_URL
: When the plugin gots approved you will get a SVN url, put it here - Add the variable
WPORG_SVN_USERNAME
: The username of your wordpress.org user - Add the variable
WPORG_SVN_PASSWORD
: The password of your wordpress.org user. You have to protect and mask it. Note: If you password does not meet the requirements of Masked Variables it does not work. It depends on you: Change your password so it works or leave it unmasked - Put some changes to
develop
branche and merge it tomaster
- The CI/CD automatically deploys to wordpress.org
When commiting to a development branch (non-master) you can automatically setup a complete virtual server with the complete WordPress installation. This is a so-called "Review application". This boilerplate is preconfigured to create such dynamic environments on the same server as your GitLab CI Runner. Here a step-by-step guide how to activate review apps for your plugin together with the Traefik router:
SSL certificate: Due to some productive usage of the boilerplate it is currently not possible to use nip.io
together with Traefik. So I have adjusted the boilerplate to use HTTP requests instead of HTTPS.
- Note: When talking in the next steps about to replace the server IP then put your IP of your GitLab CI Runner server and replace
.
with-
, for example192-168-1-250
. -Also, if you add new Traefik frontend hosts consider to not use.
because it will break Let`s Encrypt SSL certificates- - SSH into your GitLab CI Runner (see above how to configure that runner)
sudo apt install apache2-utils
: This package is necessery for the next commandhtpasswd -nb admin secure_password
: Create a password for the Traefik dashboard, replacesecure_password
with your passwordcd /opt/ && sudo nano traefik.toml
: Create a Traefik configuration file, seebuild/traefik.txt
. Copy that file and replaceyour_generated_htpasswd
with the output of the previous command. Also replaceyour_email
andyour-server-ip
sudo docker network create traefik
: Create an unique network for Traefik which is used by all containers which should be available to the web- -
sudo touch acme.json && sudo chmod 600 acme.json
: This file simply should be empty, Traefik is storing SSL certificates here- - -
sudo docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/traefik.toml:/traefik.toml -v $PWD/acme.json:/acme.json -p 80:80 -p 443:443 -l traefik.enable=true -l traefik.frontend.rule=Host:monitor-<your-server-ip>.nip.io -l traefik.port=8080 --network traefik --name traefik traefik:1.7.12-alpine
: Create the Traefik container which handles all the routing. Replace<your-server-ip>
.- sudo docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/traefik.toml:/traefik.toml -p 80:80 -l traefik.enable=true -l traefik.frontend.rule=Host:monitor-<your-server-ip>.nip.io -l traefik.port=8080 --network traefik --name traefik traefik:1.7.12-alpine
: Create the Traefik container which handles all the routing. Replace<your-server-ip>
.- Visit
monitor-<your-server-ip>.nip.io
, enter the credentials your generated withhtpasswd
and useradmin
and you will see the Traefik dashboard - Securing review apps itself? Yes, that's possible with Basic Authentication within Traeffik and also necessery for this boilerplate
- Navigate in your repository to
Settings > CI / CD > Variables
and add the variableCI_TRAEFIK_HOST
with value<your-server-ip>.nip.io
. Also replace hereyour-server-ip
- Additionally generate a new review user with
htpasswd -nb admin secure_password
and store that output as value for the GitLab CI Runner variableCI_TRAEFIK_BAUTH
. Note:$
must be doubled$$
for escaping! - Now, your GitLab CD creates dynamic environments, nice!
See also this tutorial if you want to learn more about Traefik.
Nothing.
This boilerplate is MIT licensed. Originally this boilerplate is a fork of gcorne/wp-react-boilerplate.