豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 8e5e779

Browse files
committed
build: bundle devtools frontend (#656)
1 parent 7a3b4e7 commit 8e5e779

File tree

19 files changed

+219
-178
lines changed

19 files changed

+219
-178
lines changed

.github/workflows/run-tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ jobs:
4242

4343
- name: Build
4444
run: npm run bundle
45+
env:
46+
NODE_OPTIONS: '--max_old_space_size=4096'
4547

4648
- name: Set up Node.js
4749
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
@@ -62,7 +64,7 @@ jobs:
6264
- name: Run tests
6365
shell: bash
6466
if: ${{ matrix.node != '20' }}
65-
run: npm run test
67+
run: npm run test:no-build
6668

6769
# Gating job for branch protection.
6870
test-success:

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"main": "index.js",
88
"scripts": {
99
"clean": "node -e \"require('fs').rmSync('build', {recursive: true, force: true})\"",
10-
"bundle": "npm run clean && npm run build && rollup -c rollup.config.mjs",
10+
"bundle": "npm run clean && npm run build && rollup -c rollup.config.mjs && node -e \"require('fs').rmSync('build/node_modules', {recursive: true, force: true})\"",
1111
"build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts",
1212
"typecheck": "tsc --noEmit",
1313
"format": "eslint --cache --fix . && prettier --write --cache .",
@@ -16,11 +16,12 @@
1616
"docs:generate": "node --experimental-strip-types scripts/generate-docs.ts",
1717
"start": "npm run build && node build/src/index.js",
1818
"start-debug": "DEBUG=mcp:* DEBUG_COLORS=false npm run build && node build/src/index.js",
19-
"test:node20": "node --require ./build/tests/setup.js --test-reporter spec --test-force-exit --test build/tests",
20-
"test": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test \"build/tests/**/*.test.js\"",
21-
"test:only": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
22-
"test:only:no-build": "node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
23-
"test:update-snapshots": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-force-exit --test --test-update-snapshots \"build/tests/**/*.test.js\"",
19+
"test:node20": "node --import ./build/tests/setup.js --test-reporter spec --test-force-exit --test build/tests",
20+
"test:no-build": "node --import ./build/tests/setup.js --no-warnings=ExperimentalWarning --experimental-print-required-tla --test-reporter spec --test-force-exit --test \"build/tests/**/*.test.js\"",
21+
"test": "npm run build && npm run test:no-build",
22+
"test:only": "npm run build && npm run test:only:no-build",
23+
"test:only:no-build": "node --import ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
24+
"test:update-snapshots": "npm run build && node --import ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-force-exit --test --test-update-snapshots \"build/tests/**/*.test.js\"",
2425
"prepare": "node --experimental-strip-types scripts/prepare.ts",
2526
"verify-server-json-version": "node --experimental-strip-types scripts/verify-server-json-version.ts"
2627
},

rollup.config.mjs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* and modified to specific requirement.
2020
*/
2121

22+
import fs from 'node:fs';
2223
import path from 'node:path';
2324

2425
import commonjs from '@rollup/plugin-commonjs';
@@ -79,7 +80,7 @@ const bundleDependency = (
7980
'THIRD_PARTY_NOTICES',
8081
),
8182
template(dependencies) {
82-
const stringified_dependencies = dependencies.map(dependency => {
83+
const stringifiedDependencies = dependencies.map(dependency => {
8384
let arr = [];
8485
arr.push(`Name: ${dependency.name ?? 'N/A'}`);
8586
let url = dependency.homepage ?? dependency.repository;
@@ -95,9 +96,60 @@ const bundleDependency = (
9596
}
9697
return arr.join('\n');
9798
});
99+
100+
// Manual license handling for chrome-devtools-frontend third_party
101+
const tsConfig = JSON.parse(
102+
fs.readFileSync(
103+
path.join(process.cwd(), 'tsconfig.json'),
104+
'utf-8',
105+
),
106+
);
107+
const thirdPartyDirectories = tsConfig.include.filter(location =>
108+
location.includes(
109+
'node_modules/chrome-devtools-frontend/front_end/third_party',
110+
),
111+
);
112+
113+
const manualLicenses = [];
114+
// Add chrome-devtools-frontend main license
115+
const cdtfLicensePath = path.join(
116+
process.cwd(),
117+
'node_modules/chrome-devtools-frontend/LICENSE',
118+
);
119+
if (fs.existsSync(cdtfLicensePath)) {
120+
manualLicenses.push(
121+
[
122+
'Name: chrome-devtools-frontend',
123+
'License: Apache-2.0',
124+
'',
125+
fs.readFileSync(cdtfLicensePath, 'utf-8'),
126+
].join('\n'),
127+
);
128+
}
129+
130+
for (const thirdPartyDir of thirdPartyDirectories) {
131+
const fullPath = path.join(process.cwd(), thirdPartyDir);
132+
const licenseFile = path.join(fullPath, 'LICENSE');
133+
if (fs.existsSync(licenseFile)) {
134+
const name = path.basename(thirdPartyDir);
135+
manualLicenses.push(
136+
[
137+
`Name: ${name}`,
138+
`License:`,
139+
'',
140+
fs.readFileSync(licenseFile, 'utf-8').replaceAll('\r', ''),
141+
].join('\n'),
142+
);
143+
}
144+
}
145+
146+
if (manualLicenses.length > 0) {
147+
stringifiedDependencies.push(...manualLicenses);
148+
}
149+
98150
const divider =
99151
'\n\n-------------------- DEPENDENCY DIVIDER --------------------\n\n';
100-
return stringified_dependencies.join(divider);
152+
return stringifiedDependencies.join(divider);
101153
},
102154
},
103155
},

scripts/post-build.ts

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import * as fs from 'node:fs';
88
import * as path from 'node:path';
99

10-
import tsConfig from '../tsconfig.json' with {type: 'json'};
11-
1210
import {sed} from './sed.ts';
1311

1412
const BUILD_DIR = path.join(process.cwd(), 'build');
@@ -22,29 +20,6 @@ function writeFile(filePath: string, content: string): void {
2220
fs.writeFileSync(filePath, content, 'utf-8');
2321
}
2422

25-
/**
26-
* Ensures that licenses for third party files we use gets copied into the build/ dir.
27-
*/
28-
function copyThirdPartyLicenseFiles() {
29-
const thirdPartyDirectories = tsConfig.include.filter(location => {
30-
return location.includes(
31-
'node_modules/chrome-devtools-frontend/front_end/third_party',
32-
);
33-
});
34-
35-
for (const thirdPartyDir of thirdPartyDirectories) {
36-
const fullPath = path.join(process.cwd(), thirdPartyDir);
37-
const licenseFile = path.join(fullPath, 'LICENSE');
38-
if (!fs.existsSync(licenseFile)) {
39-
console.error('No LICENSE for', path.basename(thirdPartyDir));
40-
}
41-
42-
const destinationDir = path.join(BUILD_DIR, thirdPartyDir);
43-
const destinationFile = path.join(destinationDir, 'LICENSE');
44-
fs.copyFileSync(licenseFile, destinationFile);
45-
}
46-
}
47-
4823
function main(): void {
4924
const devtoolsThirdPartyPath =
5025
'node_modules/chrome-devtools-frontend/front_end/third_party';
@@ -113,30 +88,19 @@ export const experiments = {
11388
sed(clientFile, globalAssignment, '');
11489
sed(clientFile, registerCommands, '');
11590

116-
const devtoolsLicensePath = path.join(
117-
'node_modules',
118-
'chrome-devtools-frontend',
119-
'LICENSE',
120-
);
121-
const devtoolsLicenseFileSource = path.join(
122-
process.cwd(),
123-
devtoolsLicensePath,
124-
);
125-
const devtoolsLicenseFileDestination = path.join(
126-
BUILD_DIR,
127-
devtoolsLicensePath,
128-
);
129-
fs.copyFileSync(devtoolsLicenseFileSource, devtoolsLicenseFileDestination);
130-
131-
copyThirdPartyLicenseFiles();
13291
copyDevToolsDescriptionFiles();
13392
}
13493

13594
function copyDevToolsDescriptionFiles() {
13695
const devtoolsIssuesDescriptionPath =
13796
'node_modules/chrome-devtools-frontend/front_end/models/issues_manager/descriptions';
13897
const sourceDir = path.join(process.cwd(), devtoolsIssuesDescriptionPath);
139-
const destDir = path.join(BUILD_DIR, devtoolsIssuesDescriptionPath);
98+
const destDir = path.join(
99+
BUILD_DIR,
100+
'src',
101+
'third_party',
102+
'issue-descriptions',
103+
);
140104
fs.cpSync(sourceDir, destDir, {recursive: true});
141105
}
142106

src/DevToolsConnectionAdapter.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import type {CDPConnection as devtools} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js';
8-
97
import type * as puppeteer from './third_party/index.js';
8+
import type {DevTools} from './third_party/index.js';
109
import {CDPSessionEvent} from './third_party/index.js';
1110

1211
/**
@@ -17,9 +16,11 @@ import {CDPSessionEvent} from './third_party/index.js';
1716
* We don't have to recursively listen for 'sessionattached' as the "root" CDP session sees all child session attached
1817
* events, regardless how deeply nested they are.
1918
*/
20-
export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
19+
export class PuppeteerDevToolsConnection
20+
implements DevTools.CDPConnection.CDPConnection
21+
{
2122
readonly #connection: puppeteer.Connection;
22-
readonly #observers = new Set<devtools.CDPConnectionObserver>();
23+
readonly #observers = new Set<DevTools.CDPConnection.CDPConnectionObserver>();
2324
readonly #sessionEventHandlers = new Map<
2425
string,
2526
puppeteer.Handler<unknown>
@@ -40,11 +41,14 @@ export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
4041
this.#startForwardingCdpEvents(session);
4142
}
4243

43-
send<T extends devtools.Command>(
44+
send<T extends DevTools.CDPConnection.Command>(
4445
method: T,
45-
params: devtools.CommandParams<T>,
46+
params: DevTools.CDPConnection.CommandParams<T>,
4647
sessionId: string | undefined,
47-
): Promise<{result: devtools.CommandResult<T>} | {error: devtools.CDPError}> {
48+
): Promise<
49+
| {result: DevTools.CDPConnection.CommandResult<T>}
50+
| {error: DevTools.CDPConnection.CDPError}
51+
> {
4852
if (sessionId === undefined) {
4953
throw new Error(
5054
'Attempting to send on the root session. This must not happen',
@@ -63,11 +67,11 @@ export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
6367
/* eslint-enable @typescript-eslint/no-explicit-any */
6468
}
6569

66-
observe(observer: devtools.CDPConnectionObserver): void {
70+
observe(observer: DevTools.CDPConnection.CDPConnectionObserver): void {
6771
this.#observers.add(observer);
6872
}
6973

70-
unobserve(observer: devtools.CDPConnectionObserver): void {
74+
unobserve(observer: DevTools.CDPConnection.CDPConnectionObserver): void {
7175
this.#observers.delete(observer);
7276
}
7377

@@ -99,7 +103,7 @@ export class PuppeteerDevToolsConnection implements devtools.CDPConnection {
99103
) {
100104
this.#observers.forEach(observer =>
101105
observer.onEvent({
102-
method: type as devtools.Event,
106+
method: type as DevTools.CDPConnection.Event,
103107
sessionId,
104108
params: event,
105109
}),

src/DevtoolsUtils.ts

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,11 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import {
8-
type Issue,
9-
type AggregatedIssue,
10-
type IssuesManagerEventTypes,
11-
type Target,
12-
DebuggerModel,
13-
Foundation,
14-
TargetManager,
15-
MarkdownIssueDescription,
16-
Marked,
17-
ProtocolClient,
18-
Common,
19-
I18n,
20-
} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js';
21-
227
import {PuppeteerDevToolsConnection} from './DevToolsConnectionAdapter.js';
238
import {ISSUE_UTILS} from './issue-descriptions.js';
249
import {logger} from './logger.js';
2510
import {Mutex} from './Mutex.js';
11+
import {DevTools} from './third_party/index.js';
2612
import type {
2713
Browser,
2814
Page,
@@ -88,14 +74,14 @@ function normalizeUrl(url: string): string {
8874
* A mock implementation of an issues manager that only implements the methods
8975
* that are actually used by the IssuesAggregator
9076
*/
91-
export class FakeIssuesManager extends Common.ObjectWrapper
92-
.ObjectWrapper<IssuesManagerEventTypes> {
93-
issues(): Issue[] {
77+
export class FakeIssuesManager extends DevTools.Common.ObjectWrapper
78+
.ObjectWrapper<DevTools.IssuesManagerEventTypes> {
79+
issues(): DevTools.Issue[] {
9480
return [];
9581
}
9682
}
9783

98-
export function mapIssueToMessageObject(issue: AggregatedIssue) {
84+
export function mapIssueToMessageObject(issue: DevTools.AggregatedIssue) {
9985
const count = issue.getAggregatedIssuesCount();
10086
const markdownDescription = issue.getDescription();
10187
const filename = markdownDescription?.file;
@@ -114,12 +100,14 @@ export function mapIssueToMessageObject(issue: AggregatedIssue) {
114100
let title: string | null;
115101

116102
try {
117-
processedMarkdown = MarkdownIssueDescription.substitutePlaceholders(
118-
rawMarkdown,
119-
markdownDescription.substitutions,
120-
);
121-
const markdownAst = Marked.Marked.lexer(processedMarkdown);
122-
title = MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst);
103+
processedMarkdown =
104+
DevTools.MarkdownIssueDescription.substitutePlaceholders(
105+
rawMarkdown,
106+
markdownDescription.substitutions,
107+
);
108+
const markdownAst = DevTools.Marked.Marked.lexer(processedMarkdown);
109+
title =
110+
DevTools.MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst);
123111
} catch {
124112
logger('error parsing markdown for issue ' + issue.code());
125113
return null;
@@ -138,22 +126,22 @@ export function mapIssueToMessageObject(issue: AggregatedIssue) {
138126
}
139127

140128
// DevTools CDP errors can get noisy.
141-
ProtocolClient.InspectorBackend.test.suppressRequestErrors = true;
129+
DevTools.ProtocolClient.InspectorBackend.test.suppressRequestErrors = true;
142130

143-
I18n.DevToolsLocale.DevToolsLocale.instance({
131+
DevTools.I18n.DevToolsLocale.DevToolsLocale.instance({
144132
create: true,
145133
data: {
146134
navigatorLanguage: 'en-US',
147135
settingLanguage: 'en-US',
148136
lookupClosestDevToolsLocale: l => l,
149137
},
150138
});
151-
I18n.i18n.registerLocaleDataForTest('en-US', {});
139+
DevTools.I18n.i18n.registerLocaleDataForTest('en-US', {});
152140

153141
export interface TargetUniverse {
154142
/** The DevTools target corresponding to the puppeteer Page */
155-
target: Target;
156-
universe: Foundation.Universe.Universe;
143+
target: DevTools.SDKTarget;
144+
universe: DevTools.Foundation.Universe.Universe;
157145
}
158146
export type TargetUniverseFactoryFn = (page: Page) => Promise<TargetUniverse>;
159147

@@ -232,22 +220,23 @@ export class UniverseManager {
232220
}
233221

234222
const DEFAULT_FACTORY: TargetUniverseFactoryFn = async (page: Page) => {
235-
const settingStorage = new Common.Settings.SettingsStorage({});
236-
const universe = new Foundation.Universe.Universe({
223+
const settingStorage = new DevTools.Common.Settings.SettingsStorage({});
224+
const universe = new DevTools.Foundation.Universe.Universe({
237225
settingsCreationOptions: {
238226
syncedStorage: settingStorage,
239227
globalStorage: settingStorage,
240228
localStorage: settingStorage,
241-
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
229+
settingRegistrations:
230+
DevTools.Common.SettingRegistration.getRegisteredSettings(),
242231
},
243-
overrideAutoStartModels: new Set([DebuggerModel]),
232+
overrideAutoStartModels: new Set([DevTools.DebuggerModel]),
244233
});
245234

246235
const session = await page.createCDPSession();
247236
const connection = new PuppeteerDevToolsConnection(session);
248237

249-
const targetManager = universe.context.get(TargetManager);
250-
targetManager.observeModels(DebuggerModel, SKIP_ALL_PAUSES);
238+
const targetManager = universe.context.get(DevTools.TargetManager);
239+
targetManager.observeModels(DevTools.DebuggerModel, SKIP_ALL_PAUSES);
251240

252241
const target = targetManager.createTarget(
253242
'main',
@@ -267,7 +256,7 @@ const DEFAULT_FACTORY: TargetUniverseFactoryFn = async (page: Page) => {
267256
// sent. This means DevTools can still pause, step and do whatever. We just won't
268257
// see the `Debugger.paused`/`Debugger.resumed` events on the MCP side.
269258
const SKIP_ALL_PAUSES = {
270-
modelAdded(model: DebuggerModel): void {
259+
modelAdded(model: DevTools.DebuggerModel): void {
271260
void model.agent.invoke_setSkipAllPauses({skip: true});
272261
},
273262

0 commit comments

Comments
 (0)