qwen-code/eslint.config.js
lamb 5a43efcae4
fix(editor): detect Zed.app on macOS when CLI is not in PATH (#3303)
* fix(editor): detect Zed.app on macOS when CLI is not in PATH

On macOS, Zed editor is typically installed via Homebrew or direct download,
but the CLI command 'zed' is not automatically added to PATH.

This fix adds detection for Zed.app bundle at:
- /Applications/Zed.app
- ~/Applications/Zed.app

When the CLI is not found but the app bundle exists, the code now falls back
to using the CLI inside the app bundle at Contents/MacOS/zed.

Fixes #3287

* fix(editor): use shared getEditorExecutable in useLaunchEditor

- Export getEditorExecutable() from editor.ts for use by both getDiffCommand and useLaunchEditor
- Updated useLaunchEditor.ts to use getEditorExecutable instead of its own implementation
- Updated tests to be platform-agnostic for macOS app bundle path testing
- Fixes: Zed on macOS is now detected when installed via app bundle (not just CLI in PATH)

* fix(editor): use correct Zed CLI path (Contents/MacOS/cli)

- Changed from Contents/MacOS/zed (GUI binary) to Contents/MacOS/cli (actual CLI)
- The GUI binary does not support --wait/--diff flags
- Updated tests to verify correct CLI path with regex matching for cross-platform

* style(editor): fix prettier trailing whitespace issues

Trailing spaces and array line-wrapping in zed macOS detection code.

* fix(editor): return null when editor not found + remove unused var

- getEditorExecutable now returns null (not fallback string) so
  useLaunchEditor error handling actually works
- remove unused getAppBundleCliPath in test file (typecheck fix)

* fix: add vitest globals to eslint config for test files

* fix: remove duplicate empty test with orphan toEqual call

* fix: resolve ESLint errors in editor.test.ts (arrow-body-style, no-useless-escape)

* chore: remove debug script check_braces.js

* fix: sync checkHasEditorType with getEditorExecutable, remove pr-body.md

- Replace zedAppExists() check in checkHasEditorType with
  getEditorExecutable() !== null, keeping availability detection
  and execution in sync (fixes partial install false positive)
- Remove unused zedAppExists() function
- Remove scratch file pr-body.md

* fix(editor): defer os.homedir() call to avoid breaking tests with incomplete node:os mocks

The zedMacOsPaths constant was calling homedir() at module initialization
time, which caused 'homedir is not a function' errors in CLI test files
(systemInfo.test.ts, shellCommandProcessor.test.ts) that mock node:os
without providing a homedir mock.

Fix: convert zedMacOsPaths from a constant to a lazy function
getZedAppPaths() that computes the paths only when called.

---------

Co-authored-by: lamb <906276457@qq.com>
2026-04-21 17:06:58 +08:00

334 lines
9.3 KiB
JavaScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import prettierConfig from 'eslint-config-prettier';
import importPlugin from 'eslint-plugin-import';
import vitest from '@vitest/eslint-plugin';
import globals from 'globals';
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
import storybook from 'eslint-plugin-storybook';
export default tseslint.config(
{
// Global ignores
ignores: [
'node_modules/*',
'packages/**/dist/**',
'bundle/**',
'package/bundle/**',
'.integration-tests/**',
'packages/**/.integration-test/**',
'dist/**',
'docs-site/.next/**',
'docs-site/out/**',
'.qwen/**',
],
},
eslint.configs.recommended,
...tseslint.configs.recommended,
reactHooks.configs['recommended-latest'],
reactPlugin.configs.flat.recommended,
reactPlugin.configs.flat['jsx-runtime'], // Add this if you are using React 17+
{
// Settings for eslint-plugin-react
settings: {
react: {
version: 'detect',
},
},
},
{
// Import specific config
files: ['packages/cli/src/**/*.{ts,tsx}'], // Target only TS/TSX in the cli package
plugins: {
import: importPlugin,
},
settings: {
'import/resolver': {
node: true,
},
},
rules: {
...importPlugin.configs.recommended.rules,
...importPlugin.configs.typescript.rules,
'import/no-default-export': 'warn',
'import/no-unresolved': 'off', // Disable for now, can be noisy with monorepos/paths
'import/namespace': 'off', // Disabled due to https://github.com/import-js/eslint-plugin-import/issues/2866
},
},
{
// General overrides and rules for the project (TS/TSX files)
files: ['packages/**/src/**/*.{ts,tsx}'], // Target TS/TSX in all packages (including nested)
plugins: {
import: importPlugin,
},
settings: {
'import/resolver': {
node: true,
},
},
languageOptions: {
globals: {
...globals.node,
...globals.es2021,
},
},
rules: {
// We use TypeScript for React components; prop-types are unnecessary
'react/prop-types': 'off',
// General Best Practice Rules (subset adapted for flat config)
'@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
'arrow-body-style': ['error', 'as-needed'],
curly: ['error', 'multi-line'],
eqeqeq: ['error', 'always', { null: 'ignore' }],
'@typescript-eslint/consistent-type-assertions': [
'error',
{ assertionStyle: 'as' },
],
'@typescript-eslint/explicit-member-accessibility': [
'error',
{ accessibility: 'no-public' },
],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-inferrable-types': [
'error',
{ ignoreParameters: true, ignoreProperties: true },
],
'@typescript-eslint/consistent-type-imports': [
'error',
{ disallowTypeAnnotations: false },
],
'@typescript-eslint/no-namespace': ['error', { allowDeclarations: true }],
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'import/no-internal-modules': [
'error',
{
allow: [
'react-dom/test-utils',
'react-dom/client',
'memfs/lib/volume.js',
'yargs/**',
'msw/node',
'**/generated/**',
'./styles/tailwind.css',
'./styles/App.css',
'./styles/style.css'
],
},
],
'import/no-relative-packages': 'error',
'no-cond-assign': 'error',
'no-debugger': 'error',
'no-duplicate-case': 'error',
'no-restricted-syntax': [
'error',
{
selector: 'CallExpression[callee.name="require"]',
message: 'Avoid using require(). Use ES6 imports instead.',
},
{
selector: 'ThrowStatement > Literal:not([value=/^\\w+Error:/])',
message:
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
},
],
'no-unsafe-finally': 'error',
'no-console': 'error',
'no-unused-expressions': 'off', // Disable base rule
'@typescript-eslint/no-unused-expressions': [
// Enable TS version
'error',
{ allowShortCircuit: true, allowTernary: true },
],
'no-var': 'error',
'object-shorthand': 'error',
'one-var': ['error', 'never'],
'prefer-arrow-callback': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
radix: 'error',
'default-case': 'error',
},
},
{
files: ['packages/*/src/**/*.test.{ts,tsx}', 'packages/**/test/**/*.test.{ts,tsx}'],
plugins: {
vitest,
},
languageOptions: {
globals: {
...globals.vitest,
},
},
rules: {
...vitest.configs.recommended.rules,
'vitest/expect-expect': 'off',
'vitest/no-commented-out-tests': 'off',
'no-console': 'off', // Allow console in tests
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
},
},
// extra settings for scripts that we run directly with node
{
files: ['./scripts/**/*.js', 'esbuild.config.js', 'packages/*/scripts/**/*.js'],
languageOptions: {
globals: {
...globals.node,
process: 'readonly',
console: 'readonly',
},
},
rules: {
'no-console': 'off', // Allow console in scripts
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
},
},
{
files: ['**/*.cjs'],
languageOptions: {
globals: {
...globals.node,
module: 'readonly',
require: 'readonly',
},
},
rules: {
'@typescript-eslint/no-require-imports': 'off',
'no-undef': 'off',
},
},
// ==================== no-console allowlist ====================
// The following files/packages are allowed to use console.*
// VS Code IDE companion - out of scope for no-console rule
{
files: ['packages/vscode-ide-companion/**/*.ts', 'packages/vscode-ide-companion/**/*.tsx', 'packages/vscode-ide-companion/**/*.js'],
rules: { 'no-console': 'off' },
},
// WebUI package - UI component library with Storybook
{
files: ['packages/webui/**/*.ts', 'packages/webui/**/*.tsx', 'packages/webui/**/*.js'],
rules: { 'no-console': 'off' },
},
// Specific CLI files that intentionally wrap console usage
{
files: [
'packages/cli/src/acp-integration/acpAgent.ts', // console infrastructure for ACP mode
'packages/cli/src/utils/stdioHelpers.ts', // wraps console.clear()
],
rules: { 'no-console': 'off' },
},
// Specific esbuild configs not covered by scripts pattern
{
files: ['packages/vscode-ide-companion/esbuild.js'],
languageOptions: {
globals: {
...globals.node,
process: 'readonly',
console: 'readonly',
},
},
rules: {
'no-restricted-syntax': 'off',
'@typescript-eslint/no-require-imports': 'off',
'no-console': 'off',
},
},
// Settings for web-templates assets
{
files: [
'packages/web-templates/src/**/*.{js,jsx,ts,tsx}',
'packages/web-templates/*.mjs',
],
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'no-console': 'off',
'no-undef': 'off',
},
},
// Prettier config must be last
prettierConfig,
// extra settings for scripts that we run directly with node
{
files: ['./integration-tests/**/*.{js,ts,tsx}'],
languageOptions: {
globals: {
...globals.node,
process: 'readonly',
console: 'readonly',
},
},
rules: {
'no-console': 'off', // Allow console in integration tests
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
},
},
// Settings for docs-site directory
{
files: ['docs-site/**/*.{js,jsx}'],
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
// Allow relaxed rules for documentation site
'@typescript-eslint/no-unused-vars': 'off',
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
},
},
storybook.configs['flat/recommended'],
);