Load vips library with new wordpress/worker-threads#74785
Conversation
* Add wasmInlinePlugin to inline vips * Add wasm inlining tests * doc blocks
* Add shopify/webworker to babel config * Add "@shopify/web-worker" dependency * Add vips-worker build files
* Add wasmInlinePlugin to inline vips * Add wasm inlining tests * prettier * Tests, try 2 * Improve doc blocks * plugin: match other plugin pattern * Add shopify/webworker to babel config * Add "@shopify/web-worker" dependency * Add vips-worker build files * try: add worker setup test * prettier * correct package order * Fix the build * fix @shopify/web-worker version * stub shopify/web-worker * remove console log
This new package provides utilities for type-safe Web Worker communication using an RPC (Remote Procedure Call) pattern. It allows calling methods on a worker as if they were local async functions. Key features: - wrap(): Creates a proxy for a Worker that exposes its methods as async functions - terminate(): Terminates a wrapped worker and cleans up resources - expose(): Exposes an object's methods to be called from the main thread - Automatic transferable detection for efficient ArrayBuffer transfer - Full TypeScript support with the Remote<T> type This package replaces the need for @shopify/web-worker without requiring webpack or babel, making it compatible with esbuild. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds support for a new wpWorkers field in package.json that declares
worker entry points to be bundled as self-contained JavaScript files.
Workers are bundled with all dependencies included (no externals)
since they need to be fully self-contained when loaded in a
Worker context. The WASM inlining plugin is included to support
packages like @wordpress/vips that embed WASM modules.
Example usage in package.json:
"wpWorkers": {
"./worker": "./src/worker.ts"
}
This will produce worker.mjs (ESM) and optionally worker.cjs (CJS)
in the respective build output directories.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace the @shopify/web-worker dependency with the new @wordpress/worker-threads package for Web Worker RPC communication. Changes: - Update worker.ts to use expose() from @wordpress/worker-threads - Update vips-worker.ts to use wrap() and terminate() - Add wpWorkers field to package.json for worker bundling - Make package ESM-only (remove CJS) due to wasm-vips using top-level await - Update tsconfig.json to reference the new worker-threads package The new implementation maintains the same public API while removing the dependency on webpack/babel-specific tooling. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds comprehensive test coverage for the worker-threads package: - rpc.test.ts: Tests for RPC protocol utilities (message creation, validation, posting) - transferables.test.ts: Tests for transferable object detection (ArrayBuffer, TypedArrays, nested structures, circular refs) - main-thread.test.ts: Tests for wrap() and terminate() functions - worker-thread.test.ts: Tests for expose() function Also fixes transferables.ts to check for MessagePort availability before using instanceof (not available in all environments). Total: 89 unit tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The vips-worker.ts test file was mocking @shopify/web-worker which has been removed. This test is no longer applicable since vips now uses @wordpress/worker-threads. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…74636) * Initial plan * Remove unnecessary async keywords from test callbacks Co-authored-by: adamsilverstein <2676022+adamsilverstein@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: adamsilverstein <2676022+adamsilverstein@users.noreply.github.com>
…74635) * Initial plan * Optimize transferables duplicate detection using Set Co-authored-by: adamsilverstein <2676022+adamsilverstein@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: adamsilverstein <2676022+adamsilverstein@users.noreply.github.com>
…compilation The vips package's vips-worker.ts imports from worker-code.ts, which is a generated file that's gitignored. During CI builds, after clean:packages runs, the worker-code.ts file is deleted. TypeScript compilation happens before wp-build generates the actual worker code, causing the build to fail with: "Cannot find module './worker-code'" This adds a step before TypeScript compilation that generates placeholder worker-code.ts files for packages that define wpWorkers in their package.json. The actual worker code is still generated later by wp-build, overwriting the placeholder. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
improve docs to explain build process Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Since we completely control the postMessage channel between the worker and its creator, there's no need for detailed validation of message structure. Now only checks if the type field matches a valid MessageType. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of recursively traversing all nested objects looking for transferables, now only checks direct elements in the args array (for CALL messages) or the result value (for RESULT messages). This simplification establishes an API contract that transferables (like ArrayBuffer with image data) should be passed as standalone parameters rather than nested within objects. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace custom RPC implementation with the worker-rpc library. This significantly reduces code complexity by leveraging an established library for message-passing between main and worker threads. Changes: - Add worker-rpc dependency to package.json - Simplify main-thread.ts to use RpcProvider - Simplify worker-thread.ts to use RpcProvider - Remove custom RPC message types from types.ts - Delete rpc.ts (replaced by worker-rpc) - Simplify findTransferables to accept arrays directly - Update all tests for new implementation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Switch from worker-rpc to comctx for RPC communication - Use comctx's defineProxy with adapters for main/worker threads - Remove transferables.ts since comctx handles transfers automatically with the transfer: true option - Update tests to use addEventListener pattern that comctx expects - Add comctx to jest transformIgnorePatterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| * Generate placeholder files for worker code in packages that define wpWorkers. | ||
| * This must run before TypeScript compilation because vips-worker.ts imports worker-code.ts. | ||
| */ | ||
| async function generateWorkerPlaceholders() { |
There was a problem hiding this comment.
This code is a clear candidate to be moved into its own script, somewhere in bin/packages/generate-worker-placeholders.mjs.
The bin/build.mjs script is a simple process runner that executes a series of other processes with exec, it doesn't do anything just by itself.
There was a problem hiding this comment.
Ok, makes sense - I will do that.
Sure, good suggestion - will do. |
Thanks for the additional context @jsnajdr - that helps me understand the issues raised here. |
|
And... I broke the build with the recent changes :( working on fixing that now. |
Done in 17ff565 |
Oh, I accidentally committed the build files in d985c3b. I reverted that and added them to gitignore in c5d847a to avoid that happening again. The build should be fixed now and PR ready for another review. I also merged all recent changes to the feature branch for testing (since this PR doesn't do anything testable other than build) - #74568 |
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
@jsnajdr @manzoorwanijk - this is ready for review again! |
|
cc: @adamziel if you have any feedback here |
|
Would be great if you can update changelog files as well (ref), thanks in advance! |
jsnajdr
left a comment
There was a problem hiding this comment.
I think this is good to go, I don't see any blockers. Thanks for this pioneering work 🙂
…ew worker-threads
Excellent 🎉 🎉 🎉 thank you for your support here and sharing your tips for the best approach. I'm going to merge this once tests pass which will unblock the remaining client side media PRs. As we get further into testing, we can always refine or improve what we have here now. |
| // Step 2.5: Generate worker placeholders | ||
| // This must happen before TypeScript compilation because some packages | ||
| // (like vips) have source files that import from generated worker-code.ts | ||
| await exec( 'node', [ | ||
| './bin/packages/generate-worker-placeholders.mjs', | ||
| ] ); |
There was a problem hiding this comment.
I could be wrong, but I think this may need to happen in dev.mjs too. I’m here because after a fresh pull from trunk, the dev script failed due to the missing file.
There was a problem hiding this comment.
Oh, let me review - I assume you mean running npm run dev?
There was a problem hiding this comment.
I wasn't able to reproduce in trunk, maybe something in my local. Going to try a fresh repo checkout.
There was a problem hiding this comment.
I’m here because after a fresh pull from trunk, the dev script failed due to the missing file.
@stokesman thanks for reporting this... does this go away if you run npm run build first?
I was able to reproduce on a fresh checkout, opening a PR to fix!
There was a problem hiding this comment.
@stokesman I created a follow up PR to fix this - #75104
|
Great work on this! I just wanted to flag coincident as an alternative worth considering, since it wasn't listed in the evaluated options. Why it might be interesting for WordPress:
For the current vips use case, comctx is probably the better choice since the communication pattern is simple fire-and-forget-async. But if @wordpress/worker-threads grows to cover more complex scenarios, the sync upgrade path and FFI batching could become relevant. Just wanted to make sure coincident was on the radar as an option before it's too late. |
|
Thanks for the supportive note and reference @nickchomey - I hadn't come across coincident Sounds lioke it would offer some distinct advantages especially if we widen how we are using worker threads. Will keep it in mind as we iterate. cc: @adamziel |
|
I also just came across this library. It's surely far more comprehensive than desired here, but there might be some useful ideas. |
What?
Follow on from #74478
Fixes #74352
See #69254
This PR introduces a new
@wordpress/worker-threadspackage that provides type-safe Web Worker RPC communication, and migrates@wordpress/vipsto use it instead of@shopify/web-worker.Why?
The
@shopify/web-workerpackage has several issues for Gutenberg:Alternative libraries were evaluated:
The solution is to create our own lightweight package that:
How?
New
@wordpress/worker-threadspackageProvides three main exports:
wrap(worker)- Creates a proxy for a Worker that exposes its methods as async functionsterminate(remote)- Terminates a wrapped worker and cleans up resourcesexpose(api)- Exposes an object's methods to be called from the main thread (used in worker script)Features:
Remote<T>typeThe
comctxdependencyThe
@wordpress/worker-threadspackage uses comctx as its underlying RPC mechanism. Comctx is a lightweight cross-context RPC library that provides:defineProxyAPI - Creates provider/injector pairs for RPC communication. On the worker side,provide(adapter)exposes methods; on the main thread,inject(adapter)creates a proxy to call those methods.Why comctx over other options:
The
@wordpress/worker-threadspackage wraps comctx with WordPress-specific adapters (WorkerInjectAdapterfor main thread,WorkerProvideAdapterfor worker) and adds termination handling.Build system enhancement
Added
wpWorkersfield support to@wordpress/build. Packages can now declare worker entry points in package.json:{ "wpWorkers": { "./worker": "./src/worker.ts" } }Workers are bundled as self-contained files with all dependencies included.
Migration of
@wordpress/vipsworker.tsto useexpose()from the new packagevips-worker.tsto usewrap()andterminate()@shopify/web-workerdependencyTesting Instructions
npm installto update dependenciesnpm run buildto build all packagespackages/worker-threads/build-module/contains the built filespackages/vips/build-module/contains:index.mjs- Main entry pointvips-worker.mjs- Worker API wrapperworker.mjs- Self-contained worker bundle (~17MB with WASM)Testing the worker functionality
The vips package functionality can be tested in
feature/client-side-media-dev(in #74568) through the media upload flow:npm run wp-env start