| 
 | 1 | +# Performance Lab  | 
 | 2 | + | 
 | 3 | +This is a monorepo for the WordPress Performance Team, containing a collection of standalone performance feature plugins. Refer to the [Performance Lab handbook](https://make.wordpress.org/performance/handbook/performance-lab/) for more details.   | 
 | 4 | + | 
 | 5 | +## Project Overview  | 
 | 6 | + | 
 | 7 | +* **Purpose:** To develop and maintain a suite of plugins that improve the performance of WordPress sites. All should be considered potential for future candidates for merging into WordPress core.  | 
 | 8 | +* **Technologies:** PHP, JavaScript, CSS, a variety of testing and linting tools.  | 
 | 9 | + | 
 | 10 | +### Project Structure  | 
 | 11 | + | 
 | 12 | +* `/bin`: Custom CLI commands and scripts for certain development workflows.  | 
 | 13 | +* `/plugins`: The actual WordPress plugins that are developed in this monorepo.  | 
 | 14 | +* `/plugins/*`: An individual WordPress plugin folder.  | 
 | 15 | +* `/plugins/*/tests`: PHPUnit tests for the specific WordPress plugin.  | 
 | 16 | +* `/tools`: Setup and configuration files for various tools, such as linting and testing.  | 
 | 17 | + | 
 | 18 | +## Building and Running  | 
 | 19 | + | 
 | 20 | +### Prerequisites  | 
 | 21 | + | 
 | 22 | +* [Node.js and npm](https://nodejs.org/en/)  | 
 | 23 | +* [Docker](https://www.docker.com/)  | 
 | 24 | +* [Composer](https://getcomposer.org/)  | 
 | 25 | + | 
 | 26 | +### Installation  | 
 | 27 | + | 
 | 28 | +1. Run `npm install` to install the Node.js dependencies.  | 
 | 29 | +2. Run `composer install` to install the PHP dependencies.  | 
 | 30 | +3. Run `npm run build` to do an initial build of the assets.  | 
 | 31 | + | 
 | 32 | +### Building  | 
 | 33 | + | 
 | 34 | +* To build the JavaScript and CSS assets: `npm run build`  | 
 | 35 | +* To build all plugins and place into the `build` directory: `npm run build-plugins`  | 
 | 36 | +* To build a specific plugin: `npm run build:plugin:<plugin-slug>` (e.g., `npm run build:plugin:performance-lab`)  | 
 | 37 | +* To build ZIP files for distribution: `npm run build-plugins:zip`  | 
 | 38 | + | 
 | 39 | +### Running a Local Environment  | 
 | 40 | + | 
 | 41 | +This project uses `@wordpress/env` to create a local development environment.  | 
 | 42 | + | 
 | 43 | +* Check if the environment is already running: `npm run wp-env status`  | 
 | 44 | +* Start the environment: `npm run wp-env start`  | 
 | 45 | +* Stop the environment: `npm run wp-env stop`  | 
 | 46 | + | 
 | 47 | +The environment will by default be located at `http://localhost:8888` but this can be overridden by `.wp-env.override.json`.  | 
 | 48 | + | 
 | 49 | +## Code Style  | 
 | 50 | + | 
 | 51 | +In general, the [coding standards for WordPress](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/) should be followed:  | 
 | 52 | + | 
 | 53 | +* [CSS Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/css/)  | 
 | 54 | +* [HTML Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/html/)  | 
 | 55 | +* [JavaScript Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/)  | 
 | 56 | +* [PHP Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/)  | 
 | 57 | + | 
 | 58 | +Note that for the JavaScript Coding Standards, the code should also be formatted using Prettier, specifically the [wp-prettier](https://www.npmjs.com/package/wp-prettier) fork with the `--paren-spacing` option which inserts extra spaces inside parentheses.  | 
 | 59 | + | 
 | 60 | +For the HTML Coding Standards, disregard the guidance that void/empty tags should be self-closing, such as `IMG`, `BR`, `LINK`, or `META`. This is only relevant for XML (XHTML), not HTML. So instead of `<br />` this should only use `<br>`, for example.  | 
 | 61 | + | 
 | 62 | +Additionally, the [inline documentation standards for WordPress](https://developer.wordpress.org/coding-standards/inline-documentation-standards/) should be followed:  | 
 | 63 | + | 
 | 64 | +* [PHP Documentation Standards](https://developer.wordpress.org/coding-standards/inline-documentation-standards/php/)  | 
 | 65 | +* [JavaScript Documentation Standards](https://developer.wordpress.org/coding-standards/inline-documentation-standards/javascript/)  | 
 | 66 | + | 
 | 67 | +Note that `lint-staged` will be used to automatically run code quality checks with the tooling based on the staged files.  | 
 | 68 | + | 
 | 69 | +### Indentation  | 
 | 70 | + | 
 | 71 | +In general, indentation should use tabs. Refer to `.editorconfig` in the project root for specifics.  | 
 | 72 | + | 
 | 73 | +### Inline Documentation  | 
 | 74 | + | 
 | 75 | +It is expected for new code introduced to have `@since` tags with the `n.e.x.t` placeholder version. It will get replaced with the actual version at the time of release. Do not add any code review comments to such code.  | 
 | 76 | + | 
 | 77 | +Every file, function, class, method constant, and global variable must have an associated docblock with a `@since` tag.  | 
 | 78 | + | 
 | 79 | +### PHP  | 
 | 80 | + | 
 | 81 | +Follow coding conventions in WordPress core. Namespaces are generally not used, as they are not normally used in WordPress core code. Procedural programming patterns are favored where classes play a supporting role, rather than everything being written in OOP.  | 
 | 82 | + | 
 | 83 | +Whenever possible, the most specific PHP type hints should be used, when backward compatible with PHP 7.2, the minimum version of PHP supported by WordPress and this repository. When native PHP type cannot be used, PHPStan's [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) should be used, including not only the basic types but also subtypes like `non-empty-string`, [integer ranges](https://phpstan.org/writing-php-code/phpdoc-types#integer-ranges), [general arrays](https://phpstan.org/writing-php-code/phpdoc-types#general-arrays), and especially [array shapes](https://phpstan.org/writing-php-code/phpdoc-types#array-shapes). The types should comply with PHPStan's level 10. The one exception for using PHP types is whenever a function is used as a filter. Since plugins can supply any value at all when filtering, use the expected type with a union to `mixed`. The first statement in the function in this case must always check the type, and if it is not the expected type, override it to be so.  | 
 | 84 | + | 
 | 85 | +Never render HTML `SCRIPT` tags directly in HTML. Always use the relevant APIs in WordPress for adding scripts, including `wp_enqueue_script()`, `wp_add_inline_script()`, `wp_localize_script()`, `wp_print_script_tag()`, `wp_print_inline_script_tag()`, `wp_enqueue_script_module()` among others. Favor modules over classic scripts.  | 
 | 86 | + | 
 | 87 | +Here is an example PHP file with various conventions demonstrated.  | 
 | 88 | + | 
 | 89 | +```php  | 
 | 90 | +/**  | 
 | 91 | + * Filtering functions for the Bar plugin.  | 
 | 92 | + *  | 
 | 93 | + * @since n.e.x.t  | 
 | 94 | + * @package Bar  | 
 | 95 | + */  | 
 | 96 | + | 
 | 97 | +/**  | 
 | 98 | + * Filters post title to be upper case.  | 
 | 99 | + *  | 
 | 100 | + * @since n.e.x.t  | 
 | 101 | + *  | 
 | 102 | + * @param string|mixed $title   Title.  | 
 | 103 | + * @param positive-int $post_id Post ID.  | 
 | 104 | + * @return string Upper-cased title.  | 
 | 105 | + */  | 
 | 106 | +function bar_filter_title_uppercase( $title, int $post_id ): string {  | 
 | 107 | +	if ( ! is_string( $title ) ) {  | 
 | 108 | +		$title = '';  | 
 | 109 | +	}  | 
 | 110 | +	/**  | 
 | 111 | +	 * Because plugins do bad things.  | 
 | 112 | +	 *  | 
 | 113 | +	 * @var string $title  | 
 | 114 | +	 */  | 
 | 115 | + | 
 | 116 | +	return strtoupper( $title );  | 
 | 117 | +}  | 
 | 118 | +add_filter( 'the_title', 'bar_filter_title_uppercase', 10, 2 );  | 
 | 119 | +```  | 
 | 120 | + | 
 | 121 | +### JavaScript  | 
 | 122 | + | 
 | 123 | +All JavaScript code should be written with JSDoc comments. All function parameters, return values, and other types should use [TypeScript in JSDoc](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html).  | 
 | 124 | + | 
 | 125 | +JavaScript code should be written using ES modules. This JS code must be runnable as-is without having to go through a build step, so it must be plain JavaScript and not TypeScript. The project _may_ also distribute minified versions of these JS files.  | 
 | 126 | + | 
 | 127 | +Here's an example JS file:  | 
 | 128 | + | 
 | 129 | +```js  | 
 | 130 | +/**  | 
 | 131 | + * Foo module for Optimization Detective  | 
 | 132 | + *  | 
 | 133 | + * This extension optimizes the foo performance feature.  | 
 | 134 | + *  | 
 | 135 | + * @since n.e.x.t  | 
 | 136 | + */  | 
 | 137 | + | 
 | 138 | +export const name = 'Foo';  | 
 | 139 | + | 
 | 140 | +/**  | 
 | 141 | + * @typedef {import("web-vitals").LCPMetric} LCPMetric  | 
 | 142 | + * @typedef {import("../optimization-detective/types.ts").InitializeCallback} InitializeCallback  | 
 | 143 | + * @typedef {import("../optimization-detective/types.ts").InitializeArgs} InitializeArgs  | 
 | 144 | + */  | 
 | 145 | + | 
 | 146 | +/**  | 
 | 147 | + * Initializes extension.  | 
 | 148 | + *  | 
 | 149 | + * @since n.e.x.t  | 
 | 150 | + *  | 
 | 151 | + * @type {InitializeCallback}  | 
 | 152 | + * @param {InitializeArgs} args Args.  | 
 | 153 | + */  | 
 | 154 | +export async function initialize( { log, onLCP, extendRootData } ) {  | 
 | 155 | +  onLCP(  | 
 | 156 | +    ( metric ) => {  | 
 | 157 | +      handleLCPMetric( metric, extendRootData, log );  | 
 | 158 | +    }  | 
 | 159 | +  );  | 
 | 160 | +}  | 
 | 161 | + | 
 | 162 | +// ... function definition for handleLCPMetric omitted ...  | 
 | 163 | +```  | 
 | 164 | + | 
 | 165 | +### Static Analysis Commands  | 
 | 166 | + | 
 | 167 | +* **PHPStan**: `npm run phpstan`  | 
 | 168 | +* **TypeScript**: `npm run tsc`  | 
 | 169 | + | 
 | 170 | +### Linting Commands  | 
 | 171 | + | 
 | 172 | +* **JavaScript:** `npm run lint-js`  | 
 | 173 | +* **PHP:** `npm run lint-php`  | 
 | 174 | + | 
 | 175 | +### Formatting Commands  | 
 | 176 | + | 
 | 177 | +* **JavaScript:** `npm run format-js`  | 
 | 178 | +* **PHP:** `npm run format-php`  | 
 | 179 | + | 
 | 180 | +### Testing Commands  | 
 | 181 | + | 
 | 182 | +* **End-to-end (E2E) tests:** `npm run test-e2e`  | 
 | 183 | +* **PHP tests:** `npm run test-php`  | 
 | 184 | +* **PHP tests (multisite):** `npm run test-php-multisite`  | 
0 commit comments