@cloudcatch/wp-esbuild 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +516 -0
- package/bin/wp-esbuild.mjs +36 -0
- package/lib/build-blocks-manifest.mjs +63 -0
- package/lib/build.mjs +111 -0
- package/lib/config.mjs +96 -0
- package/lib/define-config.mjs +51 -0
- package/lib/handlers/blocks.mjs +159 -0
- package/lib/handlers/copy.mjs +59 -0
- package/lib/handlers/index.mjs +44 -0
- package/lib/handlers/script.mjs +141 -0
- package/lib/handlers/scss.mjs +113 -0
- package/lib/load-config.mjs +103 -0
- package/lib/merge-esbuild-options.mjs +51 -0
- package/lib/normalize-config.mjs +267 -0
- package/lib/php-export.mjs +62 -0
- package/lib/postcss.mjs +82 -0
- package/lib/resolve-scss-outfile.mjs +75 -0
- package/lib/utils.mjs +29 -0
- package/lib/wordpress-externals-plugin.mjs +320 -0
- package/lib/write-asset-php.mjs +35 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CloudCatch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
# @cloudcatch/wp-esbuild
|
|
2
|
+
|
|
3
|
+
**Fast esbuild-based builds for WordPress plugins and themes.**
|
|
4
|
+
|
|
5
|
+
wp-esbuild is a build tool for modern WordPress development. It compiles your blocks, JavaScript, and styles into optimized assets your plugin or theme can enqueue in PHP — with sensible defaults and a config file when you need more control.
|
|
6
|
+
|
|
7
|
+
Works with the standard `src/` → `build/` layout out of the box. Supports TypeScript, script modules, SCSS, custom directory structures, and multiple build pipelines.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install --save-dev @cloudcatch/wp-esbuild
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Node.js >= 18.
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "NODE_ENV=production wp-esbuild",
|
|
21
|
+
"start": "wp-esbuild --watch"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Use `NODE_ENV=production` for minified output; omit it during development for source maps.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
### Project layout
|
|
31
|
+
|
|
32
|
+
With no config file, wp-esbuild uses this structure:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
my-plugin/
|
|
36
|
+
├── wp-esbuild.config.mjs # optional
|
|
37
|
+
├── package.json
|
|
38
|
+
├── src/
|
|
39
|
+
│ ├── blocks/
|
|
40
|
+
│ │ └── my-block/
|
|
41
|
+
│ │ ├── block.json
|
|
42
|
+
│ │ ├── index.js
|
|
43
|
+
│ │ ├── style.scss → build/blocks/my-block/style-index.css
|
|
44
|
+
│ │ ├── editor.scss → build/blocks/my-block/index.css
|
|
45
|
+
│ │ ├── view.js → build/blocks/my-block/view.js
|
|
46
|
+
│ │ └── render.php
|
|
47
|
+
│ ├── js/
|
|
48
|
+
│ │ ├── admin.js
|
|
49
|
+
│ │ └── modules/
|
|
50
|
+
│ │ └── my-module.js
|
|
51
|
+
│ └── scss/
|
|
52
|
+
│ └── main.scss
|
|
53
|
+
└── build/
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run `npm run build`. Each JS bundle gets a sibling `*.asset.php` with `dependencies` and `version`.
|
|
57
|
+
|
|
58
|
+
### Minimal config
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
// wp-esbuild.config.mjs
|
|
62
|
+
import { defineConfig } from '@cloudcatch/wp-esbuild/config';
|
|
63
|
+
|
|
64
|
+
export default defineConfig( {
|
|
65
|
+
minify: process.env.NODE_ENV === 'production',
|
|
66
|
+
sourcemap: process.env.NODE_ENV !== 'production',
|
|
67
|
+
} );
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## CLI
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npx wp-esbuild # build
|
|
74
|
+
npx wp-esbuild --watch # watch and rebuild
|
|
75
|
+
npx wp-esbuild --root ./path/to/plugin # build a specific project
|
|
76
|
+
npx wp-esbuild --blocks-manifest # regenerate blocks-manifest.php only
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
| Flag | Alias | Description |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `--root <path>` | `-r` | Project root (default: current directory). |
|
|
82
|
+
| `--watch` | `-w` | Watch for changes and rebuild. |
|
|
83
|
+
| `--blocks-manifest` | | Generate the blocks manifest only. |
|
|
84
|
+
|
|
85
|
+
## Configuration
|
|
86
|
+
|
|
87
|
+
Add `wp-esbuild.config.mjs` to your project root. Export a config object or a function:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
export default ( { env } ) => ( {
|
|
91
|
+
minify: env.NODE_ENV === 'production',
|
|
92
|
+
} );
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`defineConfig` from `@cloudcatch/wp-esbuild/config` is optional; it warns about unknown keys and invalid entry definitions.
|
|
96
|
+
|
|
97
|
+
### Two ways to define pipelines
|
|
98
|
+
|
|
99
|
+
**Shorthand keys** — `blocks`, `js`, `modules`, `scss`, and `copy`. Each accepts an object or an array of objects. These are merged with defaults and run automatically when no `entries` array is set.
|
|
100
|
+
|
|
101
|
+
**`entries` array** — explicit, named pipelines. When `entries` is provided, shorthand keys are ignored.
|
|
102
|
+
|
|
103
|
+
### Global options
|
|
104
|
+
|
|
105
|
+
| Key | Default | Description |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `srcDir` | `'src'` | Default source root for shorthand paths. |
|
|
108
|
+
| `outDir` | `'build'` | Default output root for shorthand paths. |
|
|
109
|
+
| `entries` | — | Unified pipeline list (see below). |
|
|
110
|
+
| `blocks` | see defaults | Block compilation pipeline. |
|
|
111
|
+
| `js` | `{ src: 'src/js', out: 'build/js' }` | IIFE script bundles. |
|
|
112
|
+
| `modules` | `{ src: 'src/js/modules', out: 'build/js/modules' }` | ESM script modules. |
|
|
113
|
+
| `scss` | `{ src: 'src/scss', out: 'build/css' }` | Standalone stylesheets. |
|
|
114
|
+
| `copy` | `[]` | Static file copy tasks. |
|
|
115
|
+
| `minify` | `NODE_ENV === 'production'` | Minify JS and CSS output. |
|
|
116
|
+
| `sourcemap` | `NODE_ENV !== 'production'` | Emit source maps. |
|
|
117
|
+
| `esbuild` | `{}` | Global esbuild options (`target`, `define`, `alias`, `plugins`, `loader`). |
|
|
118
|
+
| `wordpressExternals` | `{}` | Dependency extraction settings. |
|
|
119
|
+
| `postcss` | `true` | PostCSS processing for CSS (see [PostCSS](#postcss)). |
|
|
120
|
+
| `rtl` | `false` | Generate `-rtl.css` files for SCSS output. |
|
|
121
|
+
| `plugins` | `[]` | Custom build hooks (see [Build plugins](#build-plugins)). |
|
|
122
|
+
| `blocksManifest` | enabled | `blocks-manifest.php` generation (see [Blocks manifest](#blocks-manifest)). |
|
|
123
|
+
|
|
124
|
+
#### `blocksManifest`
|
|
125
|
+
|
|
126
|
+
| Key | Default | Description |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| `enabled` | `true` | Generate manifest after block builds. |
|
|
129
|
+
| `input` | `{outDir}/blocks` | Directory containing built `block.json` files. |
|
|
130
|
+
| `output` | `{outDir}/blocks/blocks-manifest.php` | Output file path. |
|
|
131
|
+
|
|
132
|
+
### Custom source and output roots
|
|
133
|
+
|
|
134
|
+
Set `srcDir` and `outDir` to avoid repeating paths:
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
export default defineConfig( {
|
|
138
|
+
srcDir: 'client',
|
|
139
|
+
outDir: 'public',
|
|
140
|
+
// shorthand defaults become client/blocks → public/blocks, etc.
|
|
141
|
+
} );
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Explicit `src` / `out` on any pipeline override these defaults.
|
|
145
|
+
|
|
146
|
+
### Shorthand pipelines
|
|
147
|
+
|
|
148
|
+
Default behavior when using shorthand keys:
|
|
149
|
+
|
|
150
|
+
| Key | Source | Output | Notes |
|
|
151
|
+
| --- | --- | --- | --- |
|
|
152
|
+
| `blocks` | `{srcDir}/blocks` | `{outDir}/blocks` | Discovers `*/block.json` |
|
|
153
|
+
| `js` | `{srcDir}/js` | `{outDir}/js` | IIFE, all `*.{js,jsx,mjs,ts,tsx}` |
|
|
154
|
+
| `modules` | `{srcDir}/js/modules` | `{outDir}/js/modules` | ESM, recursive glob |
|
|
155
|
+
| `scss` | `{srcDir}/scss` | `{outDir}/css` | Ignores partials (`**/_*`) |
|
|
156
|
+
| `copy` | — | — | `{ from, to }` paths |
|
|
157
|
+
|
|
158
|
+
Override a single admin bundle and copy static assets:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
export default defineConfig( {
|
|
162
|
+
js: {
|
|
163
|
+
src: 'src/js',
|
|
164
|
+
out: 'build/js',
|
|
165
|
+
glob: 'admin.js',
|
|
166
|
+
},
|
|
167
|
+
copy: [
|
|
168
|
+
{ from: 'src/assets', to: 'build/assets' },
|
|
169
|
+
{ from: 'src/icons/**/*.svg', to: 'build/icons', flatten: true },
|
|
170
|
+
],
|
|
171
|
+
} );
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Multiple JS output directories:
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
export default defineConfig( {
|
|
178
|
+
js: [
|
|
179
|
+
{ src: 'src/js', out: 'build/js', glob: '*.{js,jsx}' },
|
|
180
|
+
{ src: 'src/admin', out: 'build/admin', glob: '**/*.{js,ts,tsx}' },
|
|
181
|
+
],
|
|
182
|
+
} );
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Entries API
|
|
186
|
+
|
|
187
|
+
Use `entries` for full control over directory layout, nested blocks, or mixed pipeline types:
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
export default defineConfig( {
|
|
191
|
+
entries: [
|
|
192
|
+
{
|
|
193
|
+
name: 'blocks',
|
|
194
|
+
type: 'blocks',
|
|
195
|
+
src: 'src/blocks',
|
|
196
|
+
out: 'build/blocks',
|
|
197
|
+
discover: '*/block.json',
|
|
198
|
+
copy: [ 'block.json', 'render.php' ],
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'admin',
|
|
202
|
+
type: 'script',
|
|
203
|
+
src: 'src/js',
|
|
204
|
+
out: 'build/js',
|
|
205
|
+
glob: '**/*.{js,ts,tsx}',
|
|
206
|
+
format: 'iife',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'modules',
|
|
210
|
+
type: 'script',
|
|
211
|
+
src: 'src/js/modules',
|
|
212
|
+
out: 'build/js/modules',
|
|
213
|
+
glob: '**/*.{js,ts}',
|
|
214
|
+
format: 'esm',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: 'styles',
|
|
218
|
+
type: 'scss',
|
|
219
|
+
src: 'src/scss',
|
|
220
|
+
out: 'build/css',
|
|
221
|
+
glob: '**/*.scss',
|
|
222
|
+
ignore: [ '**/_*' ],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'assets',
|
|
226
|
+
type: 'copy',
|
|
227
|
+
from: 'src/assets',
|
|
228
|
+
to: 'build/assets',
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
} );
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Shared entry fields: `name`, `type`, `enabled` (set `false` to skip), `esbuild` (per-entry overrides).
|
|
235
|
+
|
|
236
|
+
## Entry types
|
|
237
|
+
|
|
238
|
+
### Blocks
|
|
239
|
+
|
|
240
|
+
Discovers block directories via a glob, then compiles each block:
|
|
241
|
+
|
|
242
|
+
| Source | Output |
|
|
243
|
+
| --- | --- |
|
|
244
|
+
| `index.js` | `index.js` + `index.asset.php` |
|
|
245
|
+
| `view.js` | `view.js` + `view.asset.php` (ESM when `viewScriptModule` is set in `block.json`) |
|
|
246
|
+
| `style.scss` | `style-index.css` |
|
|
247
|
+
| `editor.scss` | `index.css` |
|
|
248
|
+
| `block.json`, `render.php`, … | Copied as-is |
|
|
249
|
+
|
|
250
|
+
The block entry file must be named `index.js` (it can import `.ts` / `.tsx` files). Output slug is the block folder name. Nested discovery (`**/block.json`) flattens to the directory basename.
|
|
251
|
+
|
|
252
|
+
| Option | Default | Description |
|
|
253
|
+
| --- | --- | --- |
|
|
254
|
+
| `discover` | `'*/block.json'` | Glob under `src` for block.json files. |
|
|
255
|
+
| `copy` | `[ 'block.json', 'render.php' ]` | Files to copy from each block directory. |
|
|
256
|
+
| `rtl` | global setting | Generate RTL CSS for block styles. |
|
|
257
|
+
|
|
258
|
+
Reference compiled assets in `block.json` with `file:./index.js`, `file:./style-index.css`, etc.
|
|
259
|
+
|
|
260
|
+
### Scripts
|
|
261
|
+
|
|
262
|
+
Compiles files matching `glob` in `src` to `.js` in `out`.
|
|
263
|
+
|
|
264
|
+
| Option | Default | Description |
|
|
265
|
+
| --- | --- | --- |
|
|
266
|
+
| `glob` | `*.{js,jsx,mjs,ts,tsx}` | Entry file pattern. |
|
|
267
|
+
| `format` | `'iife'` | `'iife'` or `'esm'`. |
|
|
268
|
+
| `wordpressExternals` | `true` (IIFE) | Externalize `@wordpress/*` and emit `.asset.php`. |
|
|
269
|
+
| `assetPhp` | `true` | Write dependency file beside each bundle. |
|
|
270
|
+
| `extractCss` | `false` | Compile imported styles into the bundle instead of stripping them. |
|
|
271
|
+
|
|
272
|
+
Recursive globs preserve directory structure in the output.
|
|
273
|
+
|
|
274
|
+
### SCSS
|
|
275
|
+
|
|
276
|
+
Compiles standalone stylesheets (block styles are handled by the blocks pipeline).
|
|
277
|
+
|
|
278
|
+
| Option | Default | Description |
|
|
279
|
+
| --- | --- | --- |
|
|
280
|
+
| `glob` | `*.{scss,sass}` | Entry file pattern. |
|
|
281
|
+
| `ignore` | `[ '**/_*' ]` | Skip partials. |
|
|
282
|
+
| `outName` | `'preserve'` or `'flat'` | Output naming (see below). |
|
|
283
|
+
| `assetPhp` | `false` | Write `{name}.asset.php` with a content hash version. |
|
|
284
|
+
| `assetDependencies` | `[]` | Dependencies listed in CSS `.asset.php`. |
|
|
285
|
+
| `rtl` | global setting | Write `{name}-rtl.css` alongside each file. |
|
|
286
|
+
|
|
287
|
+
**`outName` values:**
|
|
288
|
+
|
|
289
|
+
| Value | Example input | Output |
|
|
290
|
+
| --- | --- | --- |
|
|
291
|
+
| `'preserve'` | `blocks/core/button.scss` | `blocks/core/button.css` |
|
|
292
|
+
| `'flat'` | `blocks/core/button.scss` | `button.css` |
|
|
293
|
+
| `{ join: '-', tail: 2 }` | `blocks/core/button.scss` | `core-button.css` |
|
|
294
|
+
| `{ join: '-' }` | `blocks/core/button.scss` | `blocks-core-button.css` |
|
|
295
|
+
|
|
296
|
+
Use `tail` to take the last N path segments (filename included) and `join` to flatten them into a single output filename. Handy when PHP expects flat CSS files derived from nested source paths.
|
|
297
|
+
|
|
298
|
+
### Copy
|
|
299
|
+
|
|
300
|
+
| Option | Description |
|
|
301
|
+
| --- | --- |
|
|
302
|
+
| `from` | Source path or glob, relative to project root. |
|
|
303
|
+
| `to` | Destination path, relative to project root. |
|
|
304
|
+
| `flatten` | When `true`, glob matches are copied flat into `to`. |
|
|
305
|
+
|
|
306
|
+
## Blocks manifest
|
|
307
|
+
|
|
308
|
+
When a blocks pipeline runs and `blocksManifest.enabled` is `true`, wp-esbuild writes a PHP file mapping block slugs to their `block.json` contents.
|
|
309
|
+
|
|
310
|
+
Register blocks in WordPress 6.7+:
|
|
311
|
+
|
|
312
|
+
```php
|
|
313
|
+
$blocks_dir = plugin_dir_path( __FILE__ ) . 'build/blocks';
|
|
314
|
+
|
|
315
|
+
wp_register_block_metadata_collection(
|
|
316
|
+
$blocks_dir,
|
|
317
|
+
$blocks_dir . '/blocks-manifest.php'
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
$manifest = require $blocks_dir . '/blocks-manifest.php';
|
|
321
|
+
foreach ( array_keys( $manifest ) as $slug ) {
|
|
322
|
+
register_block_type( $blocks_dir . '/' . $slug );
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Custom manifest paths:
|
|
327
|
+
|
|
328
|
+
```js
|
|
329
|
+
blocksManifest: {
|
|
330
|
+
input: 'public/features',
|
|
331
|
+
output: 'public/features/blocks-manifest.php',
|
|
332
|
+
},
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## WordPress externals
|
|
336
|
+
|
|
337
|
+
IIFE bundles externalize `@wordpress/*` imports to `window.wp.*` globals and list script handles in `*.asset.php`. Common npm packages map to WordPress globals:
|
|
338
|
+
|
|
339
|
+
| Import | Handle |
|
|
340
|
+
| --- | --- |
|
|
341
|
+
| `react` | `react` |
|
|
342
|
+
| `react-dom` | `react-dom` |
|
|
343
|
+
| `lodash` / `lodash-es` | `lodash` |
|
|
344
|
+
| `jquery` | `jquery` |
|
|
345
|
+
| `moment` | `moment` |
|
|
346
|
+
|
|
347
|
+
### Bundling packages
|
|
348
|
+
|
|
349
|
+
Some `@wordpress/*` packages have no script handle and are bundled by default (`@wordpress/icons`, `@wordpress/dataviews`, and others). Add more to the bundle list:
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
wordpressExternals: {
|
|
353
|
+
bundle: [ '@wordpress/icons', '@wordpress/dataviews' ],
|
|
354
|
+
},
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Force a bundled package back to external, or map custom vendors:
|
|
358
|
+
|
|
359
|
+
```js
|
|
360
|
+
wordpressExternals: {
|
|
361
|
+
external: [ '@wordpress/icons' ],
|
|
362
|
+
vendors: {
|
|
363
|
+
'my-lib': { global: 'MyLib', handle: 'my-lib' },
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Script modules
|
|
369
|
+
|
|
370
|
+
ESM bundles (`format: 'esm'`) emit module-compatible `.asset.php` files for `wp_register_script_module()`:
|
|
371
|
+
|
|
372
|
+
```php
|
|
373
|
+
return array(
|
|
374
|
+
'dependencies' => array( '@wordpress/interactivity' ),
|
|
375
|
+
'version' => '…',
|
|
376
|
+
'type' => 'module',
|
|
377
|
+
);
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Set `"viewScriptModule": "file:./view.js"` in `block.json` to build block view scripts as modules.
|
|
381
|
+
|
|
382
|
+
## PostCSS
|
|
383
|
+
|
|
384
|
+
| Value | Behavior |
|
|
385
|
+
| --- | --- |
|
|
386
|
+
| `true` | Load `postcss.config.mjs` / `.js` / `.cjs`, or use Autoprefixer. |
|
|
387
|
+
| `false` | Skip PostCSS. |
|
|
388
|
+
| `[ plugins ]` | Use a custom plugin array. |
|
|
389
|
+
|
|
390
|
+
### RTL
|
|
391
|
+
|
|
392
|
+
```js
|
|
393
|
+
export default defineConfig( { rtl: true } );
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Generates `main-rtl.css` next to each `main.css`. Enqueue when `is_rtl()`:
|
|
397
|
+
|
|
398
|
+
```php
|
|
399
|
+
wp_enqueue_style( 'my-admin', $url . 'main.css', [], $ver );
|
|
400
|
+
if ( is_rtl() ) {
|
|
401
|
+
wp_enqueue_style( 'my-admin-rtl', $url . 'main-rtl.css', [ 'my-admin' ], $ver );
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## TypeScript and JSX
|
|
406
|
+
|
|
407
|
+
`.ts`, `.tsx`, `.js`, and `.jsx` are supported out of the box. Block registration uses `index.js` as the entry point; admin and module pipelines can target `**/*.{ts,tsx}` directly.
|
|
408
|
+
|
|
409
|
+
## esbuild options
|
|
410
|
+
|
|
411
|
+
```js
|
|
412
|
+
export default defineConfig( {
|
|
413
|
+
esbuild: {
|
|
414
|
+
target: 'es2020',
|
|
415
|
+
define: { 'process.env.NODE_ENV': '"production"' },
|
|
416
|
+
alias: { '@': './src' },
|
|
417
|
+
loader: { '.svg': 'text' },
|
|
418
|
+
},
|
|
419
|
+
} );
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Defaults: `bundle: true`, `platform: 'browser'`, `target: 'es2018'`, automatic JSX, and file loaders for images and fonts.
|
|
423
|
+
|
|
424
|
+
Per-entry `esbuild` options merge on top of global settings.
|
|
425
|
+
|
|
426
|
+
## Enqueueing in PHP
|
|
427
|
+
|
|
428
|
+
**Script bundle:**
|
|
429
|
+
|
|
430
|
+
```php
|
|
431
|
+
$asset = require __DIR__ . '/build/js/admin.asset.php';
|
|
432
|
+
|
|
433
|
+
wp_enqueue_script(
|
|
434
|
+
'my-plugin-admin',
|
|
435
|
+
plugins_url( 'build/js/admin.js', __FILE__ ),
|
|
436
|
+
$asset['dependencies'],
|
|
437
|
+
$asset['version'],
|
|
438
|
+
true
|
|
439
|
+
);
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Script module:**
|
|
443
|
+
|
|
444
|
+
```php
|
|
445
|
+
$asset = require __DIR__ . '/build/js/modules/my-module.asset.php';
|
|
446
|
+
|
|
447
|
+
wp_register_script_module(
|
|
448
|
+
'my-plugin/my-module',
|
|
449
|
+
plugins_url( 'build/js/modules/my-module.js', __FILE__ ),
|
|
450
|
+
$asset['dependencies'],
|
|
451
|
+
$asset['version']
|
|
452
|
+
);
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Watch mode
|
|
456
|
+
|
|
457
|
+
`wp-esbuild --watch` debounces file changes and rebuilds affected pipelines. The blocks manifest regenerates when block output changes.
|
|
458
|
+
|
|
459
|
+
## Build plugins
|
|
460
|
+
|
|
461
|
+
Run custom steps after each build:
|
|
462
|
+
|
|
463
|
+
```js
|
|
464
|
+
export default defineConfig( {
|
|
465
|
+
plugins: [
|
|
466
|
+
{
|
|
467
|
+
name: 'my-plugin',
|
|
468
|
+
watch: [ 'config/schema.json' ],
|
|
469
|
+
async build( { projectRoot, config, entries } ) {
|
|
470
|
+
// custom build logic
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
],
|
|
474
|
+
} );
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## Programmatic API
|
|
478
|
+
|
|
479
|
+
```js
|
|
480
|
+
import { build, defineConfig } from '@cloudcatch/wp-esbuild';
|
|
481
|
+
import { buildBlocksManifest } from '@cloudcatch/wp-esbuild/blocks-manifest';
|
|
482
|
+
|
|
483
|
+
await build( process.cwd(), { watch: false } );
|
|
484
|
+
|
|
485
|
+
await buildBlocksManifest( {
|
|
486
|
+
projectRoot: process.cwd(),
|
|
487
|
+
inputDir: 'build/blocks',
|
|
488
|
+
outputFile: 'build/blocks/blocks-manifest.php',
|
|
489
|
+
} );
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
| Import | Exports |
|
|
493
|
+
| --- | --- |
|
|
494
|
+
| `@cloudcatch/wp-esbuild` | `build`, `defineConfig`, `normalizeConfig` |
|
|
495
|
+
| `@cloudcatch/wp-esbuild/config` | `defineConfig` |
|
|
496
|
+
| `@cloudcatch/wp-esbuild/blocks-manifest` | `buildBlocksManifest` |
|
|
497
|
+
| `@cloudcatch/wp-esbuild/wordpress-externals` | Externals esbuild plugins |
|
|
498
|
+
|
|
499
|
+
## Migrating from `@wordpress/scripts`
|
|
500
|
+
|
|
501
|
+
1. Replace `wp-scripts build` with `wp-esbuild` in your scripts.
|
|
502
|
+
2. Add `wp-esbuild.config.mjs` if you use custom paths or multiple bundles.
|
|
503
|
+
3. Keep block sources under `src/blocks/*/block.json` (or set `discover`).
|
|
504
|
+
4. Enqueue assets using the generated `*.asset.php` files — same format as `@wordpress/scripts`.
|
|
505
|
+
|
|
506
|
+
| `@wordpress/scripts` | wp-esbuild |
|
|
507
|
+
| --- | --- |
|
|
508
|
+
| Default entries | `blocks`, `js`, `modules`, `scss` shorthand keys |
|
|
509
|
+
| `build-blocks-manifest` | `blocksManifest.enabled` |
|
|
510
|
+
| Dependency extraction | `wordpressExternals` |
|
|
511
|
+
| PostCSS | `postcss: true` + optional config file |
|
|
512
|
+
| RTL | `rtl: true` |
|
|
513
|
+
|
|
514
|
+
## License
|
|
515
|
+
|
|
516
|
+
MIT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for @cloudcatch/wp-esbuild.
|
|
4
|
+
*/
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { parseArgs } from 'util';
|
|
7
|
+
import { build } from '../lib/build.mjs';
|
|
8
|
+
import { buildBlocksManifest } from '../lib/build-blocks-manifest.mjs';
|
|
9
|
+
import { loadProjectConfig } from '../lib/load-config.mjs';
|
|
10
|
+
|
|
11
|
+
const { values } = parseArgs( {
|
|
12
|
+
options: {
|
|
13
|
+
root: { type: 'string', short: 'r' },
|
|
14
|
+
watch: { type: 'boolean', short: 'w', default: false },
|
|
15
|
+
'blocks-manifest': { type: 'boolean', default: false },
|
|
16
|
+
},
|
|
17
|
+
allowPositionals: true,
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
const projectRoot = path.resolve( values.root || process.cwd() );
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
if ( values[ 'blocks-manifest' ] ) {
|
|
24
|
+
const config = await loadProjectConfig( projectRoot );
|
|
25
|
+
await buildBlocksManifest( {
|
|
26
|
+
projectRoot,
|
|
27
|
+
inputDir: config.blocksManifest.input,
|
|
28
|
+
outputFile: config.blocksManifest.output,
|
|
29
|
+
} );
|
|
30
|
+
} else {
|
|
31
|
+
await build( projectRoot, { watch: values.watch } );
|
|
32
|
+
}
|
|
33
|
+
} catch ( error ) {
|
|
34
|
+
console.error( error );
|
|
35
|
+
process.exit( 1 );
|
|
36
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate blocks-manifest.php from block.json files in a build directory.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors @wordpress/scripts/scripts/build-blocks-manifest.js
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import glob from 'fast-glob';
|
|
9
|
+
import { exportToPhp } from './php-export.mjs';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {object} options
|
|
13
|
+
* @param {string} options.projectRoot
|
|
14
|
+
* @param {string} options.inputDir Absolute path to scan for block.json files.
|
|
15
|
+
* @param {string} options.outputFile Absolute path for blocks-manifest.php.
|
|
16
|
+
* @return {Promise<string>} Path to the generated manifest file.
|
|
17
|
+
*/
|
|
18
|
+
export async function buildBlocksManifest( {
|
|
19
|
+
projectRoot,
|
|
20
|
+
inputDir,
|
|
21
|
+
outputFile,
|
|
22
|
+
} ) {
|
|
23
|
+
const resolvedInputDir = path.resolve( projectRoot, inputDir );
|
|
24
|
+
|
|
25
|
+
if ( ! fs.existsSync( resolvedInputDir ) ) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Blocks manifest input directory does not exist: ${ resolvedInputDir }`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const blockJsonFiles = await glob( './**/block.json', {
|
|
32
|
+
cwd: resolvedInputDir,
|
|
33
|
+
absolute: true,
|
|
34
|
+
} );
|
|
35
|
+
|
|
36
|
+
/** @type {Record<string, object>} */
|
|
37
|
+
const blocks = {};
|
|
38
|
+
|
|
39
|
+
for ( const file of blockJsonFiles ) {
|
|
40
|
+
const blockJson = JSON.parse( fs.readFileSync( file, 'utf8' ) );
|
|
41
|
+
const directoryName = path.basename( path.dirname( file ) );
|
|
42
|
+
blocks[ directoryName ] = blockJson;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ( Object.keys( blocks ).length === 0 ) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`No block.json files found in blocks manifest input: ${ resolvedInputDir }`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const phpContent = `<?php
|
|
52
|
+
// This file is generated. Do not modify it manually.
|
|
53
|
+
return ${ exportToPhp( blocks, '\t' ) };
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const resolvedOutputFile = path.resolve( projectRoot, outputFile );
|
|
57
|
+
fs.mkdirSync( path.dirname( resolvedOutputFile ), { recursive: true } );
|
|
58
|
+
fs.writeFileSync( resolvedOutputFile, phpContent );
|
|
59
|
+
|
|
60
|
+
console.log( `Block metadata PHP file generated at: ${ resolvedOutputFile }` );
|
|
61
|
+
|
|
62
|
+
return resolvedOutputFile;
|
|
63
|
+
}
|