|
15 | 15 | * (because devDependencies are installed) but fails once the package is |
16 | 16 | * bundled and published via `npm pack`. |
17 | 17 | * |
| 18 | + * The list of bundled packages is derived dynamically by scanning |
| 19 | + * `src/third_party/*.ts` for import/export statements at ESLint load time. |
| 20 | + * |
18 | 21 | * See https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1123 |
19 | 22 | */ |
20 | 23 |
|
21 | | -const THIRD_PARTY_PACKAGES = [ |
22 | | - '@modelcontextprotocol/sdk', |
23 | | - 'puppeteer-core', |
24 | | - '@puppeteer/browsers', |
25 | | - 'yargs', |
26 | | - 'debug', |
27 | | - 'zod', |
28 | | - 'core-js', |
29 | | -]; |
| 24 | +import {readdirSync, readFileSync} from 'node:fs'; |
| 25 | +import {join, dirname} from 'node:path'; |
| 26 | +import {fileURLToPath} from 'node:url'; |
| 27 | + |
| 28 | +const __dirname = dirname(fileURLToPath(import.meta.url)); |
| 29 | +const THIRD_PARTY_DIR = join(__dirname, '..', '..', 'src', 'third_party'); |
| 30 | + |
| 31 | +/** |
| 32 | + * Parse all .ts files in src/third_party/ and extract the bare package names |
| 33 | + * from import/export statements. Relative imports and node_modules paths |
| 34 | + * (used for chrome-devtools-frontend) are skipped. |
| 35 | + */ |
| 36 | +function discoverBundledPackages() { |
| 37 | + const packages = new Set(); |
| 38 | + // Match `from 'pkg'` (may appear on a different line than `import`) |
| 39 | + // and side-effect imports like `import 'pkg'`. |
| 40 | + const fromRe = /from\s+['"]([^'"]+)['"]/g; |
| 41 | + const sideEffectRe = /^import\s+['"]([^'"]+)['"]/gm; |
| 42 | + |
| 43 | + let files; |
| 44 | + try { |
| 45 | + files = readdirSync(THIRD_PARTY_DIR).filter(f => f.endsWith('.ts')); |
| 46 | + } catch { |
| 47 | + return []; |
| 48 | + } |
| 49 | + |
| 50 | + for (const file of files) { |
| 51 | + const content = readFileSync(join(THIRD_PARTY_DIR, file), 'utf8'); |
| 52 | + for (const re of [fromRe, sideEffectRe]) { |
| 53 | + re.lastIndex = 0; |
| 54 | + let match; |
| 55 | + while ((match = re.exec(content)) !== null) { |
| 56 | + const source = match[1]; |
| 57 | + // Skip relative imports and node_modules paths. |
| 58 | + if (source.startsWith('.') || source.startsWith('/')) { |
| 59 | + continue; |
| 60 | + } |
| 61 | + // Extract the bare package name (handle scoped packages like @foo/bar). |
| 62 | + const parts = source.split('/'); |
| 63 | + const pkg = source.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]; |
| 64 | + packages.add(pkg); |
| 65 | + } |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + return [...packages]; |
| 70 | +} |
| 71 | + |
| 72 | +const THIRD_PARTY_PACKAGES = discoverBundledPackages(); |
30 | 73 |
|
31 | 74 | /** Matches any import source that starts with one of the restricted packages. */ |
32 | 75 | function isRestrictedSource(source) { |
|
0 commit comments