项目文件夹

文件
Matt Van Horn cdbc66f160 chore: add ESLint guard against direct third-party imports bypassing bundle (#1189)
## Summary

- Adds a custom ESLint rule `@local/no-direct-third-party-imports` that
flags value imports of bundled third-party packages
(`@modelcontextprotocol/sdk`, `puppeteer-core`, `@puppeteer/browsers`,
`yargs`, `debug`, `zod`, `core-js`) when used outside of
`src/third_party/`
- Type-only imports (`import type`) are allowed since they are erased at
compile time and don't affect the bundle
- The rule is scoped to `src/**/*.ts` so development scripts and tests
are unaffected

This prevents the class of bugs where a direct npm import works during
development (devDependencies installed) but breaks in the published
package (only bundled code ships). PR #1111 was an example of this exact
issue caught through manual `npm pack` testing.

Closes #1123

## Test plan

- [x] Verified `npx eslint --no-cache src/` passes with no violations on
the current codebase
- [x] Verified the rule correctly catches a test file with `import
{Client} from '@modelcontextprotocol/sdk/client/index.js'`
- [x] Verified the rule allows `import type {Flags} from 'lighthouse'`
(type-only import)
- [x] Verified the rule does not fire inside `src/third_party/index.ts`
(the barrel itself)
- [x] Verified scripts/ and tests/ are unaffected (rule scoped to
`src/**/*.ts`)

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
2026-04-01 07:59:11 +00:00

162 行
3.8 KiB
JavaScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import js from '@eslint/js';
import stylisticPlugin from '@stylistic/eslint-plugin';
import {defineConfig, globalIgnores} from 'eslint/config';
import importPlugin from 'eslint-plugin-import';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import localPlugin from './scripts/eslint_rules/local-plugin.js';
export default defineConfig([
globalIgnores([
'**/node_modules',
'**/build/',
'tests/tools/fixtures/',
'src/third_party/lighthouse-devtools-mcp-bundle.js',
]),
importPlugin.flatConfigs.typescript,
{
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.node,
},
parserOptions: {
projectService: {
allowDefaultProject: [
'.prettierrc.cjs',
'puppeteer.config.cjs',
'eslint.config.mjs',
'rollup.config.mjs',
'skills/memory-leak-debugging/references/compare_snapshots.js',
],
},
},
parser: tseslint.parser,
},
plugins: {
js,
'@local': localPlugin,
'@typescript-eslint': tseslint.plugin,
'@stylistic': stylisticPlugin,
},
settings: {
'import/resolver': {
typescript: true,
},
},
extends: ['js/recommended'],
},
tseslint.configs.recommended,
tseslint.configs.stylistic,
{
name: 'TypeScript rules',
rules: {
'@local/check-license': 'error',
curly: ['error', 'all'],
'no-undef': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-explicit-any': [
'error',
{
ignoreRestArgs: true,
},
],
// This optimizes the dependency tracking for type-only files.
'@typescript-eslint/consistent-type-imports': 'error',
// So type-only exports get elided.
'@typescript-eslint/consistent-type-exports': 'error',
// Prefer interfaces over types for shape like.
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
'@typescript-eslint/array-type': [
'error',
{
default: 'array-simple',
},
],
'@typescript-eslint/no-floating-promises': 'error',
'import/order': [
'error',
{
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
},
],
'import/no-cycle': [
'error',
{
maxDepth: Infinity,
},
],
'import/enforce-node-protocol-usage': ['error', 'always'],
'@stylistic/function-call-spacing': 'error',
'@stylistic/semi': 'error',
'no-restricted-imports': [
'error',
{
patterns: [
{
regex: '.*chrome-devtools-frontend/(?!mcp/mcp.js$).*',
message:
'Import only the devtools-frontend code exported via node_modules/chrome-devtools-frontend/mcp/mcp.js',
},
],
},
],
},
},
{
name: 'Source files',
files: ['src/**/*.ts'],
rules: {
'@local/no-direct-third-party-imports': 'error',
},
},
{
name: 'Tools definitions',
files: ['src/tools/**/*.ts'],
rules: {
'@local/enforce-zod-schema': 'error',
},
},
{
name: 'Tests',
files: ['**/*.test.ts'],
rules: {
// With the Node.js test runner, `describe` and `it` are technically
// promises, but we don't need to await them.
'@typescript-eslint/no-floating-promises': 'off',
},
},
]);