[better title needed]
[insert table of contents]
WordPress uses a JavaScript task runner called Grunt to build its files. It requires the most recent LTS version of the Node.js runtime. It also requires dependencies fetched via npm. npm is packaged with Node.js, so if your computer has Node.js installed you are ready to go.
If you're not sure whether you have Node.js or the right version, run this on your command line:
node --version
npm --version
If you get a "command not found" error or an outdated version for Node:
- Windows or Mac users can download and run this Node.js installer.
- Mac users often prefer to install Node using Homebrew. After installing Homebrew, run
brew install node
to install Node.js. Alternatively, installer packages are available directly from Node.js. - Linux users can use this guide for Node.js installation on Linux.
If you get an outdated version of npm, run
npm install -g npm
.
WordPress ships with a list of dependencies, some of those are managed and updated with npm, some are managed manually.
All npm dependencies are listed in the package.json
file in the root of the project. Some dependencies are only used for development (like Grunt and code linters). Those are listed under devDependencies
. Packages that are used in the WordPress code itself are listed under dependencies
.
You can install npm dependencies by running:
npm install
The versions of the packages are locked in a package-lock.json to guarantee the same packages are installed every time you run npm install
. To update a dependency, simply run:
npm update <package_name> --save
This will update also update the package-lock.json so that others will also have the new version when they now run npm install
.
You can add a package by running:
npm install <package_name> --save
If you want to add a development package, run:
npm install <package_name> --save-dev
Both commands will also update the package-lock.json
to include the new package at a specific version.
npm installs all packages to the node_modules
directory. Sometimes we copy a dependency to WordPress and include it directly. This is configured in Gruntfile.js
under copy.npm-packages
. If you want to add a new package and include it in WordPress directly, you need to add it here.
Another way code from npm is included into WordPress is by importing distinct modules directly into our own code and have Webpack bundle it. In that case you don't need to worry about updating the build config.
Some dependencies in WordPress aren't available on npm. They are managed and updated manually. Some of them aren't updated at all anymore and / or might only still be present for backwards compatibility reasons. They are located in the src/js/_enqueues/vendor
directory, which also includes a README.md listing the sources for those dependencies.
The manual dependencies are built just like the rest of the source. This is configured in Gruntfile.js
under copy.vendor-js
.
[insert a section about developing with VVV (VVV should automatically watch changes, take away the burden entirely)]
WordPress uses grunt
to build its source. In order to start developing, you'll need to run an initial build. To do that, run the following command:
npm install && grunt build
When developing, you don't want to run grunt build
every time you change something. Instead you can run a file watcher which will automatically rebuild when a file is changed. This can be done by running the following command:
grunt watch
Important to note is that the structure of the JavaScript source is different from the structure of the JavaScript build. Historically, all JavaScript in WordPress has been scattered over two directories; wp-admin/js
and wp-includes/js
. This made it complicated to facilitate current day and modular JavaScript development practices in WordPress. That's why we've decided to introduce a unified structure for the sourcecode in src/js
. In order to maintain backwards compatibility, the structure of the build remained unchanged.
Here's an overview of how the JavaScript source code is organized:
src/js | All the JavaScript source.
├── _enqueues | Any script that ends up being enqueued.
│ ├── admin | Procedural scripts ran in the admin.
│ ├── deprecated | All deprecated scripts.
│ ├── lib | All standalone lib scripts.
│ ├── vendor | All 3rd party deps that can't be managed with NPM.
│ └── wp | All scripts that assign something to the wp namespace.
│ ├── customize | Anything under wp.customize.
│ ├── editor | Anything under wp.editor.
│ ├── media | Anything under wp.media.
│ ├── utils | Anything under wp.utils.
│ └── widgets | jQuery widgets on the wp namespace.
└── media | The media library.
As you might have noticed, most of the JavaScript is currently located in the _enqueues
directory. That's where all the scripts are that eventually get copied or built to /build
. Any independent JavaScript modules that we'll create in the future will be in src/js
in a logical structure. An early example of this is the media
folder, which contains all the separate modules for the media library. This allows us to flexibly and safely change and move around code while maintaining backwards compatibility.
WordPress core's JavaScript aims to be modular, composable and reusable. We try to stay as close as possible to ECMAScript standards, especially since transpilers like Babel and different available polyfills have already solved all major browser compatibility problems. While many modern JavaScript features might not currently be in use in WordPress, feel free to open the discussion about adding support for one in the #core-js channel on chat.wordpress.org.
In most programming languages, you can separate your code into separate modules and import those modules into your application to use the functionality contained in them. This enables developers to make code reusable and keep large projects organized. Support for modules in JavaScript wasn’t originally built into browsers, so module bundlers were created which combine all of the necessary modules into a single JavaScript file that would be loaded via a <script>
tag in the HTML. WordPress uses Webpack for this.
There are multiple ways to make JavaScript modular. WordPress uses ES6 module pattern, a standard that is gaining support in all modern browsers. There is still some code left in WordPres that uses Common JS modules. This is not a big problem because Webpack can handle those too. This will probably be refactored to ES modules in the future.
For anyone wanting to learn about JS modules, Preethi Kasireddy wrote an excellent two part introduction. You can find it here:
- Part 1: about what modules are and why you should use them.
- Part 2: about module bundling and the different ways in which that can be done.
Here are a few useful and easy to understand guidelines to write modular code:
[Explain in layman's terms + link]
[Explain in layman's terms + link]
[Might be too complex, but if we could make this easy to grasp, we're good.]
WordPress is an open source project. If we can organize our code in a way that makes it more reusable for the greater community of developers, we should try to do so.
[Documentation on WordPress packages]
Once a script is registered in core, it cannot be removed without breaking backwards compatibility. This is because plugin and theme authors unregister / replace scripts or enqueue scripts that aren't enqueued by core on certain screens. Because of modules, anything that is in a registered script can of course be extracted away into a separate module. Anything that needs to be on the global wp
object can still be assigned to it. However, using modules should eventually lead to better composition. Modules that don't need to know about global API's shouldn't depend on them or assign themselves to global objects.
WordPress is moving towards a future where JavaScript becomes more and more responsible for rendering the admin interface through a collection of single page applications. That's a different paradigm from the one which we've had for many years, where PHP was responsible for rendering the interface and JavaScript (mostly jQuery) was used to model some basic DOM interactions on top of that. These two paradigms don't fit seamlessly on top of each other. Much of the existing JavaScript will lose relevance once interfaces start getting rendered with JavaScript entirely.
A lot of functionality in the legacy JavaScript code will still be useful, but might need some refactoring to fit into the new paradigm. Think of utility functions and more self-contained libraries. It might be useful to split those up into modules to enable broader reuse. WordPress historically has always been quite critical towards code refactoring. When refactoring, always make sure to have a good usecase for a proposed change. Making something available for broader reuse could be such a usecase. To learn more about refactoring guidelines for WordPress, make sure to take a look at the handbook page about refactoring.