chore: wire forked playwright-core into workspace

This commit is contained in:
Tommy D. Rossi
2026-02-05 17:06:08 +01:00
parent 3f24ac1d8f
commit afd0840523
23 changed files with 3379 additions and 186 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "playwright"]
path = playwright
url = https://github.com/remorses/playwright.git
+72 -6
View File
@@ -175,14 +175,80 @@ if the problem was in the ws server you can restart that yourself killing proces
to run the cli locally with your current changes call `tsx playwriter/src/cli.ts -e ...`. also make sure you kill process on 19988 first to make sure to use the latest relay executor code.
## playwright source code
## playwright fork (@xmorse/playwright-core)
the playwright source code is cloned at `./tmp/playwright` (gitignored). use Task agents to explore it when you need to understand how playwright implements CDP commands, page discovery, browser connection, etc. key files:
we maintain a fork of playwright-core at `./playwright` as a git submodule. this allows us to expose frame-level CDP access (targetId/sessionId) that upstream playwright doesn't provide.
- `packages/playwright-core/src/server/chromium/` - chromium-specific CDP implementation
- `packages/playwright-core/src/server/chromium/crConnection.ts` - CDP websocket connection
- `packages/playwright-core/src/server/chromium/crBrowser.ts` - browser and page discovery
- `packages/playwright-core/src/server/chromium/chromium.ts` - connectOverCDP implementation
### submodule setup
the playwright submodule should always stay on branch `playwriter`. never switch to main or other branches.
```bash
# check current branch
cd playwright && git branch
# if not on playwriter branch
git checkout playwriter
```
### bootstrapping the repo
after cloning this repo, run bootstrap to set up the playwright submodule:
```bash
pnpm bootstrap
```
this does:
1. `git submodule update --init` - init the playwright submodule
2. `pnpm install` - install deps and link workspace packages
3. `node playwright/utils/generate_injected.js` - generate browser scripts to `src/generated/`
4. `node playwright/packages/playwright-core/build.mjs` - transpile (0.1s)
### rebuilding after changes
after modifying playwright-core source:
```bash
pnpm playwright:build # 0.1s
```
### how the simplified build works
upstream playwright bundles all dependencies into single files (zero runtime deps). we skip this by using direct dependencies instead:
**1. dependencies in package.json** - ws, debug, pngjs, commander, etc. are regular deps
**2. rewritten bundle files** - `playwright/packages/playwright-core/src/utilsBundle.ts`, `zipBundle.ts`, `mcpBundle.ts` import directly:
```ts
// before (bundled)
export const ws = require('./utilsBundleImpl').ws;
// after (direct)
import wsLibrary from 'ws';
export const ws = wsLibrary;
```
**3. simple build script** (`playwright/packages/playwright-core/build.mjs`) - just esbuild transpile + copy vendored files:
```bash
# transpile src/**/*.ts → lib/**/*.js (0.1s)
# copy third_party/lockfile.js, third_party/extract-zip.js
```
**4. generated files** - `playwright/packages/playwright-core/src/generated/*.ts` are browser scripts created by `playwright/utils/generate_injected.js`. these only need regenerating if upstream changes injected scripts.
| | upstream | ours |
|---|---|---|
| build time | ~30s | 0.1s |
| dependencies | 0 (bundled) | ~20 (external) |
| trace-viewer | built | skipped |
### key source files
- `playwright/packages/playwright-core/src/server/chromium/` - chromium CDP implementation
- `playwright/packages/playwright-core/src/server/chromium/crConnection.ts` - CDP websocket connection
- `playwright/packages/playwright-core/src/server/chromium/crBrowser.ts` - browser and page discovery
- `playwright/packages/playwright-core/src/server/chromium/chromium.ts` - connectOverCDP implementation
## ./claude-extension
+3 -1
View File
@@ -8,7 +8,9 @@
"build": "pnpm --filter playwriter --filter mcp-extension build",
"reload": "pnpm --filter playwriter build && pnpm --filter mcp-extension reload",
"agents.md": "agentsdotmd ./PLAYWRITER_AGENTS.md core.md typescript.md pnpm.md vitest.md changelog.md docs-writing.md github.md playwright.md zod.md gitchamber.md",
"release": "pnpm --filter playwriter build && cd extension && PRODUCTION=true pnpm build && cd .. && rm -f extension.zip && cd extension && zip -r ../extension.zip dist && cd .. && realpath extension.zip | pbcopy && open 'https://chrome.google.com/webstore/devconsole/a379d569-9533-44e4-9749-0368f6dbf878/jfeammnjpkecdekppnclgkkffahnhfhe/edit/package'"
"release": "pnpm --filter playwriter build && cd extension && PRODUCTION=true pnpm build && cd .. && rm -f extension.zip && cd extension && zip -r ../extension.zip dist && cd .. && realpath extension.zip | pbcopy && open 'https://chrome.google.com/webstore/devconsole/a379d569-9533-44e4-9749-0368f6dbf878/jfeammnjpkecdekppnclgkkffahnhfhe/edit/package'",
"bootstrap": "git submodule update --init && pnpm install && node playwright/utils/generate_injected.js && node playwright/packages/playwright-core/build.mjs",
"playwright:build": "node playwright/packages/playwright-core/build.mjs"
},
"devDependencies": {
"@changesets/cli": "^2.29.7",
Submodule
+1
Submodule playwright added at c5b09937d4
+1 -1
View File
@@ -53,7 +53,7 @@
"hono": "^4.10.6",
"kill-port-process": "^3.2.1",
"picocolors": "^1.1.1",
"playwright-core": "^1.56.1",
"@xmorse/playwright-core": "workspace:*",
"posthtml": "^0.16.7",
"posthtml-beautify": "^0.7.0",
"string-dedent": "^3.0.2",
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import http from 'node:http'
import net from 'node:net'
import { Page } from 'playwright-core'
import { Page } from '@xmorse/playwright-core'
import fs from 'node:fs'
import path from 'node:path'
import { getAriaSnapshot } from './aria-snapshot.js'
+1 -1
View File
@@ -1,7 +1,7 @@
// Accessibility snapshot pipeline: build raw AX tree, filter to a
// tree (interactive-only, labels/contexts, wrapper hoisting, ignored
// indent preservation), then render lines and locators.
import type { Page, Locator, ElementHandle } from 'playwright-core'
import type { Page, Locator, ElementHandle } from '@xmorse/playwright-core'
import crypto from 'node:crypto'
import fs from 'node:fs'
import path from 'node:path'
+1 -1
View File
@@ -1,5 +1,5 @@
import WebSocket from 'ws'
import type { Page } from 'playwright-core'
import type { Page } from '@xmorse/playwright-core'
import type { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js'
import type { CDPResponseBase, CDPEventBase } from './cdp-types.js'
import { getCdpUrl } from './utils.js'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Page, Locator } from 'playwright-core'
import { Page, Locator } from '@xmorse/playwright-core'
import { formatHtmlForPrompt } from './htmlrewrite.js'
import { createSmartDiff } from './diff-utils.js'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Page, Locator } from 'playwright-core'
import type { Page, Locator } from '@xmorse/playwright-core'
import type { CDPSession } from './cdp-session.js'
import type { Debugger } from './debugger.js'
import type { Editor } from './editor.js'
+1 -1
View File
@@ -3,7 +3,7 @@
* Used by both MCP and CLI to execute Playwright code with persistent state.
*/
import { Page, Frame, Browser, BrowserContext, chromium, Locator } from 'playwright-core'
import { Page, Frame, Browser, BrowserContext, chromium, Locator } from '@xmorse/playwright-core'
import crypto from 'node:crypto'
import fs from 'node:fs'
import path from 'node:path'
+1 -1
View File
@@ -1,6 +1,6 @@
import { createMCPClient } from './mcp-client.js'
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { chromium } from 'playwright-core'
import { chromium } from '@xmorse/playwright-core'
import { getCdpUrl } from './utils.js'
import { setupTestContext, cleanupTestContext, getExtensionServiceWorker, type TestContext, js } from './test-utils.js'
import './test-declarations.js'
+1 -1
View File
@@ -8,7 +8,7 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import type { Page } from 'playwright-core'
import type { Page } from '@xmorse/playwright-core'
import { createSmartDiff } from './diff-utils.js'
export interface PageMarkdownResult {
+1 -1
View File
@@ -1,7 +1,7 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import type { Page, Locator, ElementHandle } from 'playwright-core'
import type { Page, Locator, ElementHandle } from '@xmorse/playwright-core'
import type { ICDPSession, CDPSession } from './cdp-session.js'
export interface ReactSourceLocation {
+1 -1
View File
@@ -1,5 +1,5 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { chromium } from 'playwright-core'
import { chromium } from '@xmorse/playwright-core'
import { getCdpUrl } from './utils.js'
import { setupTestContext, cleanupTestContext, getExtensionServiceWorker, type TestContext, withTimeout, createSimpleServer } from './test-utils.js'
import './test-declarations.js'
+1 -1
View File
@@ -1,6 +1,6 @@
import { createMCPClient } from './mcp-client.js'
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { chromium } from 'playwright-core'
import { chromium } from '@xmorse/playwright-core'
import { getCdpUrl } from './utils.js'
import { getCDPSessionForPage } from './cdp-session.js'
import { Debugger } from './debugger.js'
+1 -1
View File
@@ -8,7 +8,7 @@
import os from 'node:os'
import path from 'node:path'
import type { Page } from 'playwright-core'
import type { Page } from '@xmorse/playwright-core'
import type {
StartRecordingResult,
StopRecordingResult,
+3 -3
View File
@@ -1,7 +1,7 @@
import { createMCPClient } from './mcp-client.js'
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { chromium } from 'playwright-core'
import type { Page } from 'playwright-core'
import { chromium } from '@xmorse/playwright-core'
import type { Page } from '@xmorse/playwright-core'
import type { AriaSnapshotNode } from './aria-snapshot.js'
import path from 'node:path'
import fs from 'node:fs'
@@ -118,7 +118,7 @@ describe('Snapshot & Screenshot Tests', () => {
"params": {
"captureBeyondViewport": false,
"clip": {
"height": 528,
"height": 581,
"scale": 1,
"width": 1280,
"x": 0,
+1 -1
View File
@@ -1,5 +1,5 @@
import type { ICDPSession, CDPSession } from './cdp-session.js'
import type { Locator } from 'playwright-core'
import type { Locator } from '@xmorse/playwright-core'
export interface StyleSource {
url: string
+2 -2
View File
@@ -2,7 +2,7 @@ import { exec } from 'node:child_process'
import { promisify } from 'node:util'
import http from 'node:http'
import net from 'node:net'
import { chromium, BrowserContext } from 'playwright-core'
import { chromium, BrowserContext } from '@xmorse/playwright-core'
import path from 'node:path'
import fs from 'node:fs'
import os from 'node:os'
@@ -317,7 +317,7 @@ export function tryJsonParse(str: string) {
* @param drainDelayMs - Time to wait for pending messages to be processed (default: 50ms)
*/
export async function safeCloseCDPBrowser(
browser: Awaited<ReturnType<typeof import('playwright-core').chromium.connectOverCDP>>,
browser: Awaited<ReturnType<typeof import('@xmorse/playwright-core').chromium.connectOverCDP>>,
drainDelayMs = 50
): Promise<void> {
// Wait for any queued message handlers to run
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Page } from 'playwright-core'
import type { Page } from '@xmorse/playwright-core'
import { sleep } from './utils.js'
const FILTERED_DOMAINS = [
+3279 -159
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,4 +1,5 @@
packages:
- ./*
- playwright/packages/playwright-core
preferWorkspacePackages: true