@wordpress/build 0.7.1-next.v.0 → 0.7.1-next.v.202602091733.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/CHANGELOG.md CHANGED
@@ -5,6 +5,8 @@
5
5
  ## 0.7.0 (2026-01-29)
6
6
 
7
7
  - Update documentation to describe `wpPlugin.name`
8
+ - Add `wpWorkers` field support for automatic worker bundling ([#74785](https://github.com/WordPress/gutenberg/pull/74785)).
9
+ - Add WASM inlining plugin for bundling WebAssembly modules.
8
10
 
9
11
  ## 0.6.0 (2026-01-16)
10
12
 
package/lib/build.mjs CHANGED
@@ -42,6 +42,11 @@ import {
42
42
  getRouteMetadata,
43
43
  generateContentEntryPoint,
44
44
  } from './route-utils.mjs';
45
+ import {
46
+ generateWorkerPlaceholder,
47
+ buildWorkers,
48
+ generateWorkerCode,
49
+ } from './worker-build.mjs';
45
50
 
46
51
  const ROOT_DIR = process.cwd();
47
52
  const PACKAGES_DIR = path.join( ROOT_DIR, 'packages' );
@@ -219,6 +224,53 @@ function createStyleBundlingPlugins( workingDir ) {
219
224
  ];
220
225
  }
221
226
 
227
+ /**
228
+ * Plugin to inline WASM files as base64 data URLs.
229
+ * This eliminates the need for separate WASM file downloads and avoids
230
+ * issues with hosts not serving WASM files with the correct MIME type.
231
+ *
232
+ * @return {Object} esbuild plugin.
233
+ */
234
+ const wasmInlinePlugin = {
235
+ name: 'wasm-inline',
236
+ setup( build ) {
237
+ // Resolve .wasm imports from node_modules.
238
+ build.onResolve( { filter: /\.wasm$/ }, async ( args ) => {
239
+ // Handle imports like 'wasm-vips/vips.wasm'.
240
+ if ( ! args.path.startsWith( '.' ) ) {
241
+ const { createRequire } = await import( 'module' );
242
+ const require = createRequire( args.resolveDir + '/index.js' );
243
+ try {
244
+ const resolved = require.resolve( args.path );
245
+ return {
246
+ path: resolved,
247
+ namespace: 'wasm-inline',
248
+ };
249
+ } catch {
250
+ // If resolution fails, let other plugins handle it.
251
+ return null;
252
+ }
253
+ }
254
+ return null;
255
+ } );
256
+
257
+ // Load WASM files and convert to base64 data URLs.
258
+ build.onLoad(
259
+ { filter: /.*/, namespace: 'wasm-inline' },
260
+ async ( args ) => {
261
+ const wasmBuffer = await readFile( args.path );
262
+ const base64 = wasmBuffer.toString( 'base64' );
263
+ const dataUrl = `data:application/wasm;base64,${ base64 }`;
264
+
265
+ return {
266
+ contents: `export default "${ dataUrl }";`,
267
+ loader: 'js',
268
+ };
269
+ }
270
+ );
271
+ },
272
+ };
273
+
222
274
  /**
223
275
  * Normalize path separators for cross-platform compatibility.
224
276
  *
@@ -1112,6 +1164,11 @@ async function transpilePackage( packageName ) {
1112
1164
 
1113
1165
  const builds = [];
1114
1166
 
1167
+ // Generate placeholder worker-code.ts if this package has wpWorkers defined
1168
+ // and the file doesn't exist yet. This is needed because transpilation happens
1169
+ // before worker bundling, but vips-worker.ts imports from worker-code.ts.
1170
+ await generateWorkerPlaceholder( packageDir, packageJson );
1171
+
1115
1172
  // Check if this is the components package that needs emotion babel plugin.
1116
1173
  // Ideally we should remove this exception and move away from emotion.
1117
1174
  const needsEmotionPlugin = packageName === 'components';
@@ -1186,6 +1243,7 @@ async function transpilePackage( packageName ) {
1186
1243
  };
1187
1244
  const plugins = [
1188
1245
  needsEmotionPlugin && emotionPlugin,
1246
+ wasmInlinePlugin,
1189
1247
  externalizeAllExceptCssPlugin,
1190
1248
  ...createStyleBundlingPlugins( packageDir ),
1191
1249
  ].filter( Boolean );
@@ -1254,6 +1312,24 @@ async function transpilePackage( packageName ) {
1254
1312
 
1255
1313
  await Promise.all( builds );
1256
1314
 
1315
+ // Build workers if wpWorkers is defined in package.json.
1316
+ // Workers are bundled as self-contained files with all dependencies included.
1317
+ await buildWorkers( packageDir, packageJson, {
1318
+ buildDir,
1319
+ buildModuleDir,
1320
+ target,
1321
+ wasmInlinePlugin,
1322
+ } );
1323
+
1324
+ // Generate inline worker code exports and re-transpile worker-code.ts.
1325
+ await generateWorkerCode( packageDir, packageName, packageJson, {
1326
+ srcDir,
1327
+ buildDir,
1328
+ buildModuleDir,
1329
+ target,
1330
+ plugins,
1331
+ } );
1332
+
1257
1333
  await compileStyles( packageName );
1258
1334
 
1259
1335
  return Date.now() - startTime;
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Worker build utilities for wp-build.
3
+ *
4
+ * Handles building worker bundles and generating inline worker code
5
+ * for packages that define wpWorkers in their package.json.
6
+ *
7
+ * @package
8
+ */
9
+
10
+ /**
11
+ * External dependencies
12
+ */
13
+ import { readFile, writeFile, access } from 'fs/promises';
14
+ import path from 'path';
15
+ import esbuild from 'esbuild';
16
+
17
+ /**
18
+ * Generate placeholder worker-code.ts for packages with wpWorkers.
19
+ *
20
+ * This must run before transpilation since worker files import worker-code.ts.
21
+ * The placeholder is later replaced with actual bundled worker content.
22
+ *
23
+ * @param {string} packageDir Path to the package directory.
24
+ * @param {Object} packageJson Parsed package.json contents.
25
+ */
26
+ export async function generateWorkerPlaceholder( packageDir, packageJson ) {
27
+ if ( ! packageJson.wpWorkers ) {
28
+ return;
29
+ }
30
+
31
+ const workerCodeFile = path.join( packageDir, 'src', 'worker-code.ts' );
32
+ try {
33
+ await access( workerCodeFile );
34
+ } catch {
35
+ // File doesn't exist, create placeholder
36
+ const placeholderContent = `/**
37
+ * Worker code for inline Blob URL creation.
38
+ *
39
+ * This file is a placeholder that gets overwritten by the build process.
40
+ * If you see this placeholder content at runtime, run \`npm run build\` first.
41
+ *
42
+ * @package gutenberg
43
+ */
44
+
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ export const workerCode = '/* Placeholder - run npm run build to generate actual worker code */';
47
+ `;
48
+ await writeFile( workerCodeFile, placeholderContent );
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Build worker bundles (ESM and CJS) for packages with wpWorkers.
54
+ *
55
+ * Workers are bundled as self-contained files with all dependencies included.
56
+ *
57
+ * @param {string} packageDir Path to the package directory.
58
+ * @param {Object} packageJson Parsed package.json contents.
59
+ * @param {Object} options Build options.
60
+ * @param {string} options.buildDir Path to the CJS build directory.
61
+ * @param {string} options.buildModuleDir Path to the ESM build-module directory.
62
+ * @param {string[]} options.target esbuild target configuration.
63
+ * @param {Object} options.wasmInlinePlugin The WASM inline plugin for esbuild.
64
+ */
65
+ export async function buildWorkers(
66
+ packageDir,
67
+ packageJson,
68
+ { buildDir, buildModuleDir, target, wasmInlinePlugin }
69
+ ) {
70
+ if ( ! packageJson.wpWorkers ) {
71
+ return;
72
+ }
73
+
74
+ const workerBuilds = [];
75
+ const workerEntries =
76
+ typeof packageJson.wpWorkers === 'object' &&
77
+ packageJson.wpWorkers !== null
78
+ ? Object.entries( packageJson.wpWorkers )
79
+ : [];
80
+
81
+ for ( const [ outputName, entryPath ] of workerEntries ) {
82
+ const workerEntryPoint = path.join( packageDir, entryPath );
83
+ const workerOutputName = outputName.replace( /^\.\//, '' );
84
+
85
+ // Build ESM worker for build-module (primary for browser use).
86
+ if ( packageJson.module ) {
87
+ workerBuilds.push(
88
+ esbuild.build( {
89
+ entryPoints: [ workerEntryPoint ],
90
+ outfile: path.join(
91
+ buildModuleDir,
92
+ `${ workerOutputName }.mjs`
93
+ ),
94
+ bundle: true,
95
+ format: 'esm',
96
+ platform: 'browser',
97
+ target,
98
+ sourcemap: true,
99
+ // Bundle everything - workers need to be self-contained.
100
+ external: [],
101
+ plugins: [ wasmInlinePlugin ],
102
+ define: {
103
+ 'process.env.NODE_ENV': JSON.stringify(
104
+ process.env.NODE_ENV || 'production'
105
+ ),
106
+ },
107
+ } )
108
+ );
109
+ }
110
+
111
+ // Build CJS worker for the `build` directory (Node.js compatibility).
112
+ //
113
+ // Note: We only generate CJS worker bundles when the package exposes a
114
+ // CommonJS entry point via `packageJson.main`. Packages that are ESM-only
115
+ // or browser-focused (for example, packages that have removed their
116
+ // `main` field like `@wordpress/vips`) will not produce CJS worker
117
+ // outputs, and will instead rely solely on the ESM worker built into
118
+ // `build-module`. This conditional is intentional to avoid creating
119
+ // unused or misleading CJS artifacts.
120
+ if ( packageJson.main ) {
121
+ workerBuilds.push(
122
+ esbuild.build( {
123
+ entryPoints: [ workerEntryPoint ],
124
+ outfile: path.join( buildDir, `${ workerOutputName }.cjs` ),
125
+ bundle: true,
126
+ format: 'cjs',
127
+ platform: 'node',
128
+ target,
129
+ sourcemap: true,
130
+ external: [],
131
+ plugins: [ wasmInlinePlugin ],
132
+ define: {
133
+ 'process.env.NODE_ENV': JSON.stringify(
134
+ process.env.NODE_ENV || 'production'
135
+ ),
136
+ },
137
+ } )
138
+ );
139
+ }
140
+ }
141
+
142
+ await Promise.all( workerBuilds );
143
+ }
144
+
145
+ /**
146
+ * Generate inline worker code exports and re-transpile worker-code.ts.
147
+ *
148
+ * Creates worker-code.ts with bundled content for Blob URL loading,
149
+ * then re-transpiles it to the output directories.
150
+ *
151
+ * @param {string} packageDir Path to the package directory.
152
+ * @param {string} packageName Name of the package.
153
+ * @param {Object} packageJson Parsed package.json contents.
154
+ * @param {Object} options Build options.
155
+ * @param {string} options.srcDir Path to the source directory.
156
+ * @param {string} options.buildDir Path to the CJS build directory.
157
+ * @param {string} options.buildModuleDir Path to the ESM build-module directory.
158
+ * @param {string[]} options.target esbuild target configuration.
159
+ * @param {Array} options.plugins esbuild plugins for transpilation.
160
+ */
161
+ export async function generateWorkerCode(
162
+ packageDir,
163
+ packageName,
164
+ packageJson,
165
+ { srcDir, buildDir, buildModuleDir, target, plugins }
166
+ ) {
167
+ if ( ! packageJson.wpWorkers ) {
168
+ return;
169
+ }
170
+
171
+ const workerEntries =
172
+ typeof packageJson.wpWorkers === 'object' &&
173
+ packageJson.wpWorkers !== null
174
+ ? Object.entries( packageJson.wpWorkers )
175
+ : [];
176
+
177
+ // Generate inline worker code exports for each worker.
178
+ // This allows the worker code to be bundled inline and loaded via Blob URL,
179
+ // which works even when import.meta.url is not available (e.g., webpack bundles).
180
+ for ( const [ outputName ] of workerEntries ) {
181
+ const workerOutputName = outputName.replace( /^\.\//, '' );
182
+ const workerOutputPath = path.join(
183
+ buildModuleDir,
184
+ `${ workerOutputName }.mjs`
185
+ );
186
+
187
+ try {
188
+ const workerContent = await readFile( workerOutputPath, 'utf8' );
189
+ const workerCodeFile = path.join(
190
+ packageDir,
191
+ 'src',
192
+ 'worker-code.ts'
193
+ );
194
+ const workerCodeContent = `/**
195
+ * Worker code for inline Blob URL creation.
196
+ *
197
+ * This file is auto-generated by the build process.
198
+ * Do not edit manually - run \`npm run build\` to regenerate.
199
+ *
200
+ * @package gutenberg
201
+ */
202
+
203
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
204
+ export const workerCode = ${ JSON.stringify( workerContent ) };
205
+ `;
206
+ await writeFile( workerCodeFile, workerCodeContent );
207
+ } catch ( error ) {
208
+ console.warn(
209
+ `Warning: Could not generate worker-code.ts for ${ packageName }:`,
210
+ error.message
211
+ );
212
+ }
213
+ }
214
+
215
+ // Re-transpile worker-code.ts to output directories.
216
+ // The initial transpilation used placeholder content because worker
217
+ // bundling hadn't completed yet. Now that src/worker-code.ts contains
218
+ // the real bundled worker code, re-transpile it so that
219
+ // build-module/worker-code.mjs (and build/worker-code.cjs if applicable)
220
+ // reflect the actual worker code in a single build run.
221
+ const workerCodeSrcFile = path.join( packageDir, 'src', 'worker-code.ts' );
222
+ const retranspileBuilds = [];
223
+
224
+ if ( packageJson.module ) {
225
+ retranspileBuilds.push(
226
+ esbuild.build( {
227
+ entryPoints: [ workerCodeSrcFile ],
228
+ outdir: buildModuleDir,
229
+ outbase: srcDir,
230
+ outExtension: { '.js': '.mjs' },
231
+ bundle: true,
232
+ platform: 'neutral',
233
+ format: 'esm',
234
+ sourcemap: true,
235
+ target,
236
+ jsx: 'automatic',
237
+ jsxImportSource: 'react',
238
+ loader: { '.js': 'jsx' },
239
+ plugins,
240
+ } )
241
+ );
242
+ }
243
+
244
+ if ( packageJson.main ) {
245
+ retranspileBuilds.push(
246
+ esbuild.build( {
247
+ entryPoints: [ workerCodeSrcFile ],
248
+ outdir: buildDir,
249
+ outbase: srcDir,
250
+ outExtension: { '.js': '.cjs' },
251
+ bundle: true,
252
+ platform: 'node',
253
+ format: 'cjs',
254
+ sourcemap: true,
255
+ target,
256
+ jsx: 'automatic',
257
+ jsxImportSource: 'react',
258
+ loader: { '.js': 'jsx' },
259
+ plugins,
260
+ } )
261
+ );
262
+ }
263
+
264
+ await Promise.all( retranspileBuilds );
265
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/build",
3
- "version": "0.7.1-next.v.0+5aba098fc",
3
+ "version": "0.7.1-next.v.202602091733.0+daa0b19c4",
4
4
  "description": "Build tool for WordPress plugins.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -35,7 +35,7 @@
35
35
  "wp-build": "./lib/build.mjs"
36
36
  },
37
37
  "dependencies": {
38
- "@emotion/babel-plugin": "11.11.0",
38
+ "@emotion/babel-plugin": "11.13.5",
39
39
  "autoprefixer": "10.4.21",
40
40
  "browserslist-to-esbuild": "2.1.1",
41
41
  "change-case": "4.1.2",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "d730f9e00f5462d1b9d2660632850f5f43ccff44"
80
+ "gitHead": "74f59922b25e30904319373dda91bf8e81f3544e"
81
81
  }
package/tsconfig.json CHANGED
@@ -22,6 +22,7 @@
22
22
  "exclude": [
23
23
  // Excluded to maintain consistency with bin/tsconfig.json
24
24
  // where packages/build.mjs was also excluded
25
- "lib/build.mjs"
25
+ "lib/build.mjs",
26
+ "lib/worker-build.mjs"
26
27
  ]
27
28
  }