mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-11-06 13:13:22 +00:00
The chrome-devtools CLI client starts a new process for every command. It imported src/third_party/index.js, the full bundle with Puppeteer, the MCP SDK and the DevTools frontend. It also imported src/index.ts for logDisclaimers, which loads the whole server. One CLI call loaded 79 modules and spent about 670 ms of CPU before it did any work. Add src/third_party/cli.ts with the modules the CLI path needs: yargs, semver, PipeTransport and the URLPattern polyfill. Rollup bundles it as a separate file, build/src/third_party/cli.js. The index bundle treats ./cli.js as external and re-exports from it, so the server code keeps one import path. Move logDisclaimers to src/utils/disclaimers.ts. src/index.ts re-exports it, so the public API does not change. ConfigParser, daemon/client, check-for-updates, url and the CLI entry import from third_party/cli.js. src/utils/url.ts imports URLPattern instead of using the global, so it works on Node 22 without the full bundle. A new test, tests/cli-imports.test.ts, walks the import graph of the built CLI entry and fails if it reaches third_party/index.js. THIRD_PARTY_NOTICES lists the same packages in a different order. Results (bundled build): - One CLI call loads 21 modules instead of 79. - CPU time to start one CLI call: 667 ms -> 158 ms. - Import cost: third_party/index.js 552 ms, third_party/cli.js 67 ms. - cli.js is 349 KB. - The 9 CLI test files (tests/e2e/*, tests/daemon/client.test.ts) on 2 cores, one file at a time: wall 65.9 s -> 49.6 s (-25%), CPU of all child processes 143.5 s -> 108.0 s (-25%).
62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2026 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import assert from 'node:assert';
|
|
import {readFileSync} from 'node:fs';
|
|
import path from 'node:path';
|
|
import {describe, it} from 'node:test';
|
|
|
|
const BUILD_SRC = path.resolve(import.meta.dirname, '..', 'src');
|
|
const CLI_ENTRY = path.join(BUILD_SRC, 'bin', 'chrome-devtools.js');
|
|
const FULL_BUNDLE = path.join(BUILD_SRC, 'third_party', 'index.js');
|
|
|
|
const IMPORT_RE = /\b(?:from|import)\s*\(?\s*['"](\.{1,2}\/[^'"]+)['"]/g;
|
|
|
|
/**
|
|
* Walks the static and dynamic relative imports of the built JS files and
|
|
* returns the first path from `entry` to `target`, or undefined.
|
|
*/
|
|
function findImportPath(entry: string, target: string): string[] | undefined {
|
|
const parents = new Map<string, string | undefined>([[entry, undefined]]);
|
|
const queue = [entry];
|
|
while (queue.length > 0) {
|
|
const file = queue.shift();
|
|
if (file === undefined) {
|
|
break;
|
|
}
|
|
if (file === target) {
|
|
const chain = [];
|
|
for (let f: string | undefined = file; f; f = parents.get(f)) {
|
|
chain.unshift(path.relative(BUILD_SRC, f));
|
|
}
|
|
return chain;
|
|
}
|
|
if (!file.startsWith(BUILD_SRC)) {
|
|
continue;
|
|
}
|
|
const source = readFileSync(file, 'utf8');
|
|
for (const match of source.matchAll(IMPORT_RE)) {
|
|
const next = path.resolve(path.dirname(file), match[1]);
|
|
if (!parents.has(next)) {
|
|
parents.set(next, file);
|
|
queue.push(next);
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
describe('chrome-devtools CLI imports', () => {
|
|
it('does not load the full third-party bundle', () => {
|
|
const chain = findImportPath(CLI_ENTRY, FULL_BUNDLE);
|
|
assert.strictEqual(
|
|
chain,
|
|
undefined,
|
|
`The CLI must import from third_party/cli.js only. Import chain:\n ${chain?.join('\n -> ')}`,
|
|
);
|
|
});
|
|
});
|