From c60559642e1aca78ed7e546b9ebe2eb98c443704 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 20 May 2025 01:09:09 +0000
Subject: [PATCH] refactor: Modify playground to use vanilla JavaScript
This commit refactors the existing playground in `packages/playground` to use pure vanilla JavaScript, removing React and its associated dependencies.
Changes include:
- Removal of React, ReactDOM, and related type/plugin dependencies from `packages/playground/package.json`.
- Deletion of React-specific files (`App.tsx`, `main.tsx`, `index.css`, `postcss.config.mjs`).
- Recreation of `index.html` with a new structure suitable for a vanilla JS application.
- Creation of `src/main.js` containing the client-side logic for interacting with `comlink-worker-pool`, managing UI elements, and displaying logs and stats without React.
- Update of `vite.config.ts` to remove React plugin and ensure compatibility with a vanilla JS/TS Vite setup.
- Confirmation that the existing GitHub deployment workflow (`deploy-playground.yml`) is still compatible with the vanilla JS Vite project.
The playground now operates without React, directly using DOM manipulation for UI updates and `comlink-worker-pool` for managing worker threads.
---
.github/workflows/deploy-playground-react.yml | 27 +++++
package.json | 1 +
packages/playground-react/README.md | 3 +
packages/playground-react/index.html | 12 ++
packages/playground-react/package.json | 30 +++++
.../postcss.config.mjs | 0
.../src/App.tsx | 41 ++-----
.../src/index.css | 0
.../src/main.tsx | 0
packages/playground-react/src/worker.ts | 38 ++++++
packages/playground-react/tsconfig.json | 22 ++++
packages/playground-react/vite.config.ts | 15 +++
packages/playground/index.html | 86 +++++++++++--
packages/playground/package.json | 5 -
packages/playground/src/main.js | 114 ++++++++++++++++++
packages/playground/vite.config.ts | 5 +-
16 files changed, 354 insertions(+), 45 deletions(-)
create mode 100644 .github/workflows/deploy-playground-react.yml
create mode 100644 packages/playground-react/README.md
create mode 100644 packages/playground-react/index.html
create mode 100644 packages/playground-react/package.json
rename packages/{playground => playground-react}/postcss.config.mjs (100%)
rename packages/{playground => playground-react}/src/App.tsx (93%)
rename packages/{playground => playground-react}/src/index.css (100%)
rename packages/{playground => playground-react}/src/main.tsx (100%)
create mode 100644 packages/playground-react/src/worker.ts
create mode 100644 packages/playground-react/tsconfig.json
create mode 100644 packages/playground-react/vite.config.ts
create mode 100644 packages/playground/src/main.js
diff --git a/.github/workflows/deploy-playground-react.yml b/.github/workflows/deploy-playground-react.yml
new file mode 100644
index 0000000..0f6d34c
--- /dev/null
+++ b/.github/workflows/deploy-playground-react.yml
@@ -0,0 +1,27 @@
+name: Deploy React Playground
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+jobs:
+ build-and-deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v1
+ - run: bun install --frozen-lockfile
+ # Ensure comlink-worker-pool (dependency for the react hook) and the react hook itself are built
+ - run: bun run -F comlink-worker-pool build
+ - run: bun run -F comlink-worker-pool-react build
+ # Build the React playground
+ - run: bun run -F @comlink-worker-pool/playground-react build
+ - name: Deploy React Playground to GitHub Pages
+ id: deployment-react-playground # Changed ID as requested
+ uses: peaceiris/actions-gh-pages@v4
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: ./packages/playground-react/dist # Updated publish_dir
+ repository: natanelia/comlink-plus # This should ideally be dynamic or checked if it's correct
diff --git a/package.json b/package.json
index 0411967..d9b1b6d 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"playground:lint": "bun run -F comlink-worker-pool-playground lint",
"playground:build": "bun run -F comlink-worker-pool-playground build",
"playground:dev": "bun run -F comlink-worker-pool-playground dev",
+ "dev:react-playground": "bun --filter @comlink-worker-pool/playground-react dev",
"lint": "bun run -F comlink-worker-pool-react -F comlink-worker-pool lint",
"build": "bun run -F comlink-worker-pool-react -F comlink-worker-pool build",
"build:watch": "bun run -F comlink-worker-pool-react -F comlink-worker-pool build",
diff --git a/packages/playground-react/README.md b/packages/playground-react/README.md
new file mode 100644
index 0000000..48600a6
--- /dev/null
+++ b/packages/playground-react/README.md
@@ -0,0 +1,3 @@
+# Playground React
+
+This package is a playground for React components.
diff --git a/packages/playground-react/index.html b/packages/playground-react/index.html
new file mode 100644
index 0000000..5af464e
--- /dev/null
+++ b/packages/playground-react/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Comlink Worker Pool React Playground
+
+
+
+
+
+
diff --git a/packages/playground-react/package.json b/packages/playground-react/package.json
new file mode 100644
index 0000000..26cfe50
--- /dev/null
+++ b/packages/playground-react/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "@comlink-worker-pool/playground-react",
+ "private": true,
+ "scripts": {
+ "dev": "vite",
+ "dev:react": "vite",
+ "build": "vite build",
+ "preview": "vite preview",
+ "lint": "biome check ."
+ },
+ "dependencies": {
+ "react": "^19.1.0",
+ "react-dom": "^19.1.0",
+ "comlink": "^4.4.2",
+ "comlink-worker-pool": "workspace:packages/comlink-worker-pool",
+ "comlink-worker-pool-react": "*"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4.1.4",
+ "@types/bun": "^1.2.10",
+ "@types/react": "^19.1.2",
+ "@types/react-dom": "^19.1.2",
+ "@vitejs/plugin-react": "^4.4.1",
+ "autoprefixer": "^10.4.21",
+ "oxlint": "^0.16.6",
+ "postcss": "^8.5.3",
+ "tailwindcss": "^4.1.4",
+ "vite": "^6.3.2"
+ }
+}
diff --git a/packages/playground/postcss.config.mjs b/packages/playground-react/postcss.config.mjs
similarity index 100%
rename from packages/playground/postcss.config.mjs
rename to packages/playground-react/postcss.config.mjs
diff --git a/packages/playground/src/App.tsx b/packages/playground-react/src/App.tsx
similarity index 93%
rename from packages/playground/src/App.tsx
rename to packages/playground-react/src/App.tsx
index 164f5e1..1d0444b 100644
--- a/packages/playground/src/App.tsx
+++ b/packages/playground-react/src/App.tsx
@@ -1,5 +1,6 @@
import * as Comlink from "comlink";
-import { WorkerPool, type WorkerPoolStats } from "comlink-worker-pool";
+import { useWorkerPool } from "comlink-worker-pool-react";
+import type { WorkerPoolStats } from "comlink-worker-pool";
import { useEffect, useRef, useState } from "react";
import "./index.css";
@@ -15,7 +16,6 @@ type WorkerApi = {
const proxyFactory = (worker: Worker) => Comlink.wrap(worker);
function App() {
- const [pool, setPool] = useState | null>(null);
const [inputNumber, setInputNumber] = useState(40);
const [taskCount, setTaskCount] = useState(10);
const [inputText, setInputText] = useState("");
@@ -23,28 +23,11 @@ function App() {
const [logs, setLogs] = useState<{ key: string; text: string }[]>([]);
const logsListRef = useRef(null);
- useEffect(() => {
- const size = navigator.hardwareConcurrency || 4;
- const p = new WorkerPool({
- size,
- workerFactory,
- proxyFactory,
- onUpdateStats: setStats,
- workerIdleTimeoutMs: 1000,
- });
- setPool(p);
- setStats(p.getStats());
- return () => {
- p.terminateAll();
- };
- }, []);
-
- const [stats, setStats] = useState({
- size: 0,
- available: 0,
- queue: 0,
- workers: 0,
- idleWorkers: 0,
+ const { pool, stats, setStats } = useWorkerPool({
+ size: navigator.hardwareConcurrency || 4,
+ workerFactory,
+ proxyFactory,
+ workerIdleTimeoutMs: 1000,
});
// Utility to format log messages
@@ -138,31 +121,31 @@ function App() {
Workers
- {stats.size}
+ {stats?.size ?? 0}
Available
- {stats.available}
+ {stats?.available ?? 0}
Queue
- {stats.queue}
+ {stats?.queue ?? 0}
Active Workers
- {stats.workers}
+ {stats?.workers ?? 0}
Idle Workers
- {stats.idleWorkers}
+ {stats?.idleWorkers ?? 0}
diff --git a/packages/playground/src/index.css b/packages/playground-react/src/index.css
similarity index 100%
rename from packages/playground/src/index.css
rename to packages/playground-react/src/index.css
diff --git a/packages/playground/src/main.tsx b/packages/playground-react/src/main.tsx
similarity index 100%
rename from packages/playground/src/main.tsx
rename to packages/playground-react/src/main.tsx
diff --git a/packages/playground-react/src/worker.ts b/packages/playground-react/src/worker.ts
new file mode 100644
index 0000000..51f701f
--- /dev/null
+++ b/packages/playground-react/src/worker.ts
@@ -0,0 +1,38 @@
+import * as Comlink from "comlink";
+
+// CPU-intensive Fibonacci
+function fib(n: number): number {
+ if (n <= 1) return n;
+ return fib(n - 1) + fib(n - 2);
+}
+
+// simulate variable workload
+function sleep(ms: number) {
+ return new Promise((res) => setTimeout(res, ms));
+}
+
+export async function fibAsync(n: number): Promise {
+ if (typeof n !== "number") throw new Error("Input must be a number");
+ return fib(n);
+}
+
+// Example API methods for proxified callbacks
+export async function countWords(text: string): Promise {
+ // Simulate processing delay
+ const delay = Math.floor(Math.random() * 800) + 200;
+ await sleep(delay);
+ if (typeof text !== "string") throw new Error("Input must be a string");
+ return text.trim().split(/\s+/).filter(Boolean).length;
+}
+
+export async function reverseString(text: string): Promise {
+ const delay = Math.floor(Math.random() * 800) + 200;
+ await sleep(delay);
+ if (typeof text !== "string") throw new Error("Input must be a string");
+ return text.split("").reverse().join("");
+}
+
+const api = { fibAsync, countWords, reverseString };
+export type WorkerApi = typeof api;
+
+Comlink.expose(api);
diff --git a/packages/playground-react/tsconfig.json b/packages/playground-react/tsconfig.json
new file mode 100644
index 0000000..a6ba4cc
--- /dev/null
+++ b/packages/playground-react/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "types": ["bun-types"],
+ "outDir": "dist",
+ "declaration": true,
+ "declarationDir": "dist/types",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "jsx": "react-jsx",
+ "allowJs": true,
+ "checkJs": false,
+ "resolveJsonModule": true,
+ "isolatedModules": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/packages/playground-react/vite.config.ts b/packages/playground-react/vite.config.ts
new file mode 100644
index 0000000..bb31027
--- /dev/null
+++ b/packages/playground-react/vite.config.ts
@@ -0,0 +1,15 @@
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+export default defineConfig({
+ base: '/comlink-plus/',
+ build: {
+ outDir: "./dist",
+ emptyOutDir: true,
+ },
+ server: {
+ open: true,
+ port: 5174,
+ },
+ plugins: [react()],
+});
diff --git a/packages/playground/index.html b/packages/playground/index.html
index 64c38fd..4195002 100644
--- a/packages/playground/index.html
+++ b/packages/playground/index.html
@@ -1,12 +1,80 @@
-
-
-
- Comlink Worker Pool Playground
-
-
-
-
-
+
+
+
+
+ Comlink Worker Pool - Vanilla JS Playground
+
+
+
+
+
+
Comlink Worker Pool - Vanilla JS Playground
+
+
+
Controls
+
+
+
+
+
+
+
+
+
+
Fibonacci Calculator
+
+
+
+
+
+
+
Word Counter
+
+
+
+
+
+
+
String Reverser
+
+
+
+
+
+
+
+
Logs
+
+
+
+
+
+
diff --git a/packages/playground/package.json b/packages/playground/package.json
index 78339b3..fb727b1 100644
--- a/packages/playground/package.json
+++ b/packages/playground/package.json
@@ -8,17 +8,12 @@
"lint": "biome check ."
},
"dependencies": {
- "react": "^19.1.0",
- "react-dom": "^19.1.0",
"comlink": "^4.4.2",
"comlink-worker-pool": "workspace:packages/comlink-worker-pool"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.4",
"@types/bun": "^1.2.10",
- "@types/react": "^19.1.2",
- "@types/react-dom": "^19.1.2",
- "@vitejs/plugin-react": "^4.4.1",
"autoprefixer": "^10.4.21",
"oxlint": "^0.16.6",
"postcss": "^8.5.3",
diff --git a/packages/playground/src/main.js b/packages/playground/src/main.js
new file mode 100644
index 0000000..14bad55
--- /dev/null
+++ b/packages/playground/src/main.js
@@ -0,0 +1,114 @@
+import * as Comlink from "comlink";
+import { WorkerPool } from "comlink-worker-pool";
+
+// DOM Elements
+const taskCountInput = document.getElementById("taskCount");
+const fibInput = document.getElementById("fibInput");
+const wordCountInput = document.getElementById("wordCountInput");
+const reverseStringInput = document.getElementById("reverseStringInput");
+
+const runFibButton = document.getElementById("runFib");
+const runCountWordsButton = document.getElementById("runCountWords");
+const runReverseStringButton = document.getElementById("runReverseString");
+
+const logsContainer = document.getElementById("logsContainer");
+const clearLogsButton = document.getElementById("clearLogs");
+
+const statSize = document.getElementById("statSize");
+const statAvailable = document.getElementById("statAvailable");
+const statQueue = document.getElementById("statQueue");
+const statWorkers = document.getElementById("statWorkers");
+const statIdleWorkers = document.getElementById("statIdleWorkers");
+
+let pool;
+
+const workerFactory = () =>
+ new Worker(new URL("./worker.ts", import.meta.url), { type: "module" });
+
+const proxyFactory = (worker) => Comlink.wrap(worker);
+
+function updateStatsDisplay(stats) {
+ if (!stats) return;
+ statSize.textContent = stats.size;
+ statAvailable.textContent = stats.available;
+ statQueue.textContent = stats.queue;
+ statWorkers.textContent = stats.workers;
+ statIdleWorkers.textContent = stats.idleWorkers;
+}
+
+function initializePool() {
+ const size = navigator.hardwareConcurrency || 4;
+ pool = new WorkerPool({
+ size,
+ workerFactory,
+ proxyFactory,
+ onUpdateStats: updateStatsDisplay,
+ workerIdleTimeoutMs: 1000,
+ });
+ updateStatsDisplay(pool.getStats());
+}
+
+function addLog(message) {
+ const logEntry = document.createElement("div");
+ logEntry.className = "log-entry";
+ logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
+ logsContainer.appendChild(logEntry);
+ logsContainer.scrollTop = logsContainer.scrollHeight; // Auto-scroll
+}
+
+function clearLogs() {
+ logsContainer.innerHTML = "";
+}
+
+clearLogsButton.addEventListener("click", clearLogs);
+
+async function runAndLogTasks(taskFn, label, input) {
+ if (!pool) return;
+ const api = pool.getApi();
+ const numTasks = parseInt(taskCountInput.value, 10);
+ addLog(`Starting ${numTasks} "${label}" tasks for input: ${input}...`);
+
+ const tasks = [];
+ for (let i = 0; i < numTasks; i++) {
+ tasks.push(
+ (async () => {
+ try {
+ const result = await taskFn(api, input);
+ addLog(`"${label}" result: ${result}`);
+ } catch (error) {
+ addLog(`"${label}" error: ${error.message}`);
+ console.error(error);
+ }
+ })(),
+ );
+ }
+ await Promise.all(tasks);
+ addLog(`Finished ${numTasks} "${label}" tasks.`);
+}
+
+runFibButton.addEventListener("click", () => {
+ const n = parseInt(fibInput.value, 10);
+ runAndLogTasks(async (api, val) => api.fibAsync(val), "Fibonacci", n);
+});
+
+runCountWordsButton.addEventListener("click", () => {
+ const text = wordCountInput.value;
+ runAndLogTasks(async (api, val) => api.countWords(val), "Count Words", text);
+});
+
+runReverseStringButton.addEventListener("click", () => {
+ const text = reverseStringInput.value;
+ runAndLogTasks(async (api, val) => api.reverseString(val), "Reverse String", text);
+});
+
+// Initialize
+initializePool();
+
+// Handle page unload to terminate workers
+window.addEventListener("beforeunload", () => {
+ if (pool) {
+ pool.terminateAll();
+ }
+});
+
+addLog("Playground initialized. Worker pool created.");
diff --git a/packages/playground/vite.config.ts b/packages/playground/vite.config.ts
index 8b3418f..a2b7c61 100644
--- a/packages/playground/vite.config.ts
+++ b/packages/playground/vite.config.ts
@@ -1,4 +1,3 @@
-import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
@@ -9,6 +8,8 @@ export default defineConfig({
},
server: {
open: true,
+ port: 5173, // Default port for the non-React playground
},
- plugins: [react()],
+ // No React-specific plugins needed for the vanilla JS playground
+ plugins: [],
});