mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-10-19 04:41:56 +00:00
* chore(desktop): rename packages/desktop-shell to packages/desktop The Electron-era packages/desktop was removed in #9085 and the OpenWork downstream has forked, so the short name is free and the shell suffix is the only thing left of the transition. Rename the package directory and every load-bearing reference: workspace negations (npm + pnpm), the ci.yml crate filter, desktop-release.yml, the isolation guard, lint/ignore configs, scripts tests, CLI workspace-fixture tests, and living docs. User-facing identity is untouched: the crate stays qwen-code-desktop, productName stays "Qwen Code Desktop", the bundle identifier, updater feed, release tag scheme, and the legacy electron-bridge asset names are all unchanged. check-desktop-isolation now guards both path names so the old one cannot re-enter a workspace set. Dated design docs under docs/design/ keep their historical paths deliberately. Refs #8596 * docs(design): follow the rename in the undated notification design notes The web-shell-browser-notification-details pair is a living doc without a date prefix, so it falls under the PR's update rule rather than the historical-record exception. * docs(desktop): stop citing paths the rename invalidated Three prose sites and one CI comment still described the pre-rename world, or contradicted themselves once `packages/desktop` named both the removed Electron app and the living Tauri shell. - ci.yml `desktop_shell`: the comment this branch added credited the Cargo.toml guard with keeping "branches from either side of the rename" green. It does not. A pre-rename head's files sit under `packages/desktop-shell`, which the changed-files filter no longer matches, so the job skips before the guard ever runs. Say that, and say what the guard does cover (a head the filter matched because ci.yml itself changed, with no crate at the new path). - docs/design/web-shell-user-message-links.md: "desktop-shell routing" / "desktop-shell clicks" -> "desktop-host", the wording this branch already chose in LinkifiedText.tsx for the same rule. - docs/design/telemetry-runtime-client-attribution-design.md: repoint the `runtime.rs` citation. It backs the live `QWEN_CODE_DESKTOP=1` claim, and the other document of record for that invariant -- the comment in acp-channel-fallback.ts -- was already repointed by this branch, so the two disagreed. Only the path token changes; the doc's dated findings are untouched. - brand-create.mjs / SKILL.md / src-tauri/src/main.rs: name the historical referent as historical (the Electron `packages/desktop` removed in PR 9085) instead of letting it read as a self-reference. In main.rs the citation `packages/desktop/packages/shared/src/config/storage.ts` now looked like a subpath of the file's own package. Rewritten, not deleted: the `QWEN_DEFAULT_WORKSPACE_DIR` clause next to it is the only in-code documentation of that override and still resolves. Deliberately not swept: `scripts/check-desktop-isolation.js`'s `packages/desktop-shell` tripwire, ci.yml's #8132 history comment, `docs/design/9152-architecture-invariant-classification.md` (names both spellings and is accurate), and the date-prefixed `docs/design/2026-07-31-desktop-web-shell-release.md`. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmug1iw422v * test(scripts): pin the desktop crate path and the isolation guard's coverage Both invariants fail green today, and this rename is the second time the crate directory has moved. `scripts/check-desktop-isolation.js` gained `'packages/desktop'` in `nativePrefixes` -- the only production-logic line in this branch -- and nothing read it. Deleting that line leaves the guard's output and exit code byte-identical, because `isNativeLocation('packages/desktop')` then matches no remaining prefix; a later PR that drops `!packages/desktop` from the root workspace list would re-admit the package with CI green. The `desktop_shell` job names the crate directory in five places (the changed-files filter, the Cargo.toml guard and its `::notice::` text, the rust-cache `workspaces:`, and two `working-directory:` values). Nothing asserted they agree with each other or with a directory that exists, so the next drift reports a skipped job as a passed one. The new suite parses both instead of re-running them, and is superset-shaped on purpose: `nativePrefixes` carries a fourth entry (the `packages/desktop-shell` tripwire) that nothing negates, and an exact-match assertion would invite deleting it. The npm/pnpm mirror of the negation list is already pinned by package-scripts.test.js, so this reads package.json only. Verified by mutation, each applied alone and then restored: reverting the filter alternative or either `working-directory` to `packages/desktop-shell`, dropping `'packages/desktop'` from `nativePrefixes`, and dropping `!packages/desktop` from package.json all turn this suite red. Today no other test does. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmug1iw422v * test(scripts): pin what the desktop-isolation tests actually claim Four review findings on scripts/tests/desktop-isolation.test.js, each verified by mutating the thing it claims to pin, before and after. - The comment above the local `isNativeLocation` copy claimed a change to the script's rule "has to be made here too". Nothing reads the rule back from the producer -- `nativePrefixesFromSource` parses the array literal only -- and narrowing the script's rule to prefix equality leaves the suite green. Describe the copy instead of claiming coupling that does not exist. - `still carries the Cargo.toml existence guard` matched only the guard's condition text. Flip the body's `changed=false` to `changed=true`, or move it after `fi`, and the suite stayed green at 5/5 while the job runs every gated step against a crate that is not there. Assert the assignment sits inside the guard's own `if ... fi` body. - Both on-disk checks keyed on the bare directory, so git-ignored residue under `packages/desktop-shell` (`node_modules`, which survives a branch switch with an empty `git status`) reddened two tests on a tree with no defect and blamed the root workspace set. Key on `package.json`, which is what `packages/*` requires to make a directory a workspace member; a real re-entry still reddens both. - The `startsWith(filterAlternative)` assertion compared a path built from `crateDir` -- `filterAlternative` with one trailing slash stripped -- back against `filterAlternative`, so it could not fail for any input. Dropping the trailing slash from the changed-files filter in ci.yml left it green. Replace it with the property that makes the alternative a path prefix rather than a bare string prefix. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmugedurp3j --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
697 lines
21 KiB
JavaScript
697 lines
21 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';
|
|
import checkFile from 'eslint-plugin-check-file';
|
|
import noCoreRootBarrelImport, {
|
|
CORE_BARREL_SPECIFIERS,
|
|
} from './eslint-rules/no-core-root-barrel-import.js';
|
|
import noUtilsUpwardImport from './eslint-rules/no-utils-upward-import.js';
|
|
import noCoreUtilsUpwardImport from './eslint-rules/no-core-utils-upward-import.js';
|
|
import { legacyFilenames } from './eslint.legacy-filenames.mjs';
|
|
import { legacyCoreBarrelImports } from './eslint.legacy-core-barrel-imports.mjs';
|
|
import noConfigObjectCreate from './eslint-rules/no-config-object-create.js';
|
|
|
|
// General syntax restrictions applied to every TS/TSX source file. Hoisted so
|
|
// surface-specific overrides (flat config keeps only the last
|
|
// no-restricted-syntax setting per file) can repeat them without drift.
|
|
const generalRestrictedSyntaxSelectors = [
|
|
{
|
|
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.',
|
|
},
|
|
];
|
|
|
|
// Undeclared imports are checked in shipped sources only: tests resolve
|
|
// shared tooling such as vitest from the root manifest by design.
|
|
const extraneousDependencyTestFiles = [
|
|
'**/*.test.{ts,tsx}',
|
|
'**/*.spec.{ts,tsx}',
|
|
'**/__tests__/**',
|
|
'**/test/**',
|
|
'**/tests/**',
|
|
];
|
|
const extraneousDependencyOptions = {
|
|
devDependencies: true,
|
|
optionalDependencies: true,
|
|
peerDependencies: true,
|
|
};
|
|
|
|
export default tseslint.config(
|
|
{
|
|
// Global ignores
|
|
ignores: [
|
|
'node_modules/*',
|
|
'packages/**/dist/**',
|
|
'packages/web-templates/src/generated/**',
|
|
// Generated UTS #39 confusables table (6.5k-entry literal): the
|
|
// type-aware rules OOM the eslint heap on it, and generated data
|
|
// has no idiom to enforce. Regenerate via
|
|
// packages/web-shell/scripts/generate-confusables.mjs.
|
|
'packages/web-shell/client/utils/unicodeConfusables.ts',
|
|
'integrations/**/dist/**',
|
|
'bundle/**',
|
|
'package/bundle/**',
|
|
'.integration-tests/**',
|
|
'packages/**/.integration-test/**',
|
|
'dist/**',
|
|
'demo/**/dist/**',
|
|
'docs-site/.next/**',
|
|
'docs-site/out/**',
|
|
'.qwen/**',
|
|
'scripts/codemod/fixtures/**', // codemod test data; intentionally non-idiomatic ink input/output
|
|
'packages/desktop/runtime/**',
|
|
'packages/core/src/skills/bundled/browser-use/runtime/**',
|
|
'packages/desktop/src-tauri/target/**',
|
|
'packages/live-host/**', // standalone Electron app with its own Node test conventions
|
|
'packages/cua-driver/**', // vendored trycua/cua driver (Rust + scripts); not qwen-code TS
|
|
'packages/mobile-mcp/**', // vendored mobile-next/mobile-mcp; has own eslint config
|
|
],
|
|
},
|
|
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
|
|
},
|
|
},
|
|
{
|
|
// ACP integration and the daemon are separate runtime surfaces that happen
|
|
// to share a package directory. ACP may consume neutral contracts under
|
|
// `runtime/`, but never `serve/` implementation modules — see #8084.
|
|
files: ['packages/cli/src/acp-integration/**/*.{ts,tsx,js}'],
|
|
rules: {
|
|
'no-restricted-imports': [
|
|
'error',
|
|
{
|
|
patterns: [
|
|
{
|
|
group: ['**/serve', '**/serve/**'],
|
|
message:
|
|
'acp-integration must not import serve/ internals. Put shared, lifecycle-free logic in packages/cli/src/runtime/ instead (#8084).',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// `utils/` is the leaf layer that every other directory imports, so it
|
|
// must not import back up into a domain directory. Type-only imports are
|
|
// exempt: they are erased at compile time and cannot create a runtime
|
|
// cycle. See #9146.
|
|
files: ['packages/cli/src/utils/**/*.{ts,tsx}'],
|
|
plugins: {
|
|
architecture: {
|
|
rules: {
|
|
'no-utils-upward-import': noUtilsUpwardImport,
|
|
},
|
|
},
|
|
},
|
|
rules: {
|
|
'architecture/no-utils-upward-import': 'error',
|
|
},
|
|
},
|
|
{
|
|
// General overrides and rules for the project (TS/TSX files)
|
|
files: [
|
|
'packages/**/src/**/*.{ts,tsx}',
|
|
'integrations/**/src/**/*.{ts,tsx}',
|
|
],
|
|
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',
|
|
'mime/lite',
|
|
'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', ...generalRestrictedSyntaxSelectors],
|
|
'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',
|
|
},
|
|
},
|
|
{
|
|
// A package must declare what its own sources import. npm and the hoisted
|
|
// pnpm layout both resolve a sibling's or the root's dependency, so a
|
|
// missing declaration stays invisible until the package is installed on
|
|
// its own — the check an isolated node_modules layout would add. Type-only
|
|
// imports are exempt because they disappear at build time.
|
|
files: [
|
|
'packages/**/src/**/*.{ts,tsx}',
|
|
'integrations/**/src/**/*.{ts,tsx}',
|
|
// web-shell is published and keeps its shipped sources in client/, not
|
|
// src/, so the globs above reach none of it.
|
|
'packages/web-shell/client/**/*.{ts,tsx}',
|
|
],
|
|
ignores: extraneousDependencyTestFiles,
|
|
rules: {
|
|
'import/no-extraneous-dependencies': [
|
|
'error',
|
|
extraneousDependencyOptions,
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// export-html and insight carry a package.json only for "type" and their
|
|
// build script; web-templates declares what they import.
|
|
files: ['packages/web-templates/src/**/*.{ts,tsx}'],
|
|
ignores: extraneousDependencyTestFiles,
|
|
rules: {
|
|
'import/no-extraneous-dependencies': [
|
|
'error',
|
|
{
|
|
...extraneousDependencyOptions,
|
|
packageDir: `${import.meta.dirname}/packages/web-templates`,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// The rule itself exempts tests, __tests__, and fixtures; repeating that
|
|
// here would give the exemption two sources of truth. The utils-upward
|
|
// rule self-scopes to packages/core/src/utils production files, so it can
|
|
// share this block without redefining the architecture plugin.
|
|
files: ['packages/core/src/**/*.{ts,tsx}'],
|
|
plugins: {
|
|
architecture: {
|
|
rules: {
|
|
'no-core-root-barrel-import': noCoreRootBarrelImport,
|
|
'no-core-utils-upward-import': noCoreUtilsUpwardImport,
|
|
},
|
|
},
|
|
},
|
|
rules: {
|
|
'architecture/no-core-root-barrel-import': 'error',
|
|
'architecture/no-core-utils-upward-import': 'error',
|
|
},
|
|
},
|
|
{
|
|
// Importing a value from core's package root evaluates all of core in
|
|
// every test whose import graph reaches the importing file, which is
|
|
// where most cli test time goes (#10908). Type-only imports are erased
|
|
// and stay allowed. Files that predate this rule are listed in
|
|
// eslint.legacy-core-barrel-imports.mjs; drop an entry when migrating it.
|
|
files: ['packages/cli/src/**/*.{ts,tsx}'],
|
|
ignores: [
|
|
'**/*.{test,spec}.{ts,tsx}',
|
|
'**/__tests__/**',
|
|
'**/fixtures/**',
|
|
...legacyCoreBarrelImports,
|
|
],
|
|
rules: {
|
|
'@typescript-eslint/no-restricted-imports': [
|
|
'error',
|
|
{
|
|
paths: [...CORE_BARREL_SPECIFIERS].map((name) => ({
|
|
name,
|
|
allowTypeImports: true,
|
|
message:
|
|
"Import from the core module that defines the symbol, e.g. '@qwen-code/qwen-code-core/utils/debugLogger.js'. The package root evaluates all of core in every test that reaches this file (#10908).",
|
|
})),
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// no-restricted-imports only sees static import/export declarations, so a
|
|
// dynamic `await import('../serve/...')` would slip past the #8084 guard
|
|
// above. Kept after the general TS block because flat config applies only
|
|
// the last no-restricted-syntax setting per file, hence the repeated
|
|
// general selectors.
|
|
files: ['packages/cli/src/acp-integration/**/*.{ts,tsx,js}'],
|
|
rules: {
|
|
'no-restricted-syntax': [
|
|
'error',
|
|
...generalRestrictedSyntaxSelectors,
|
|
{
|
|
// \x2f is '/' — esquery selector regexes cannot contain a literal '/'.
|
|
selector: "ImportExpression[source.value=/(^|\\x2f)serve(\\x2f|$)/i]",
|
|
message:
|
|
'acp-integration must not dynamically import serve/ internals. Put shared, lifecycle-free logic in packages/cli/src/runtime/ instead (#8084).',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
'packages/web-shell/client/**/*.{ts,tsx}',
|
|
'packages/web-shell/*.config.ts',
|
|
],
|
|
plugins: {
|
|
import: importPlugin,
|
|
},
|
|
settings: {
|
|
'import/resolver': {
|
|
node: true,
|
|
},
|
|
},
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.browser,
|
|
...globals.es2021,
|
|
...globals.node,
|
|
},
|
|
},
|
|
rules: {
|
|
'react/prop-types': 'off',
|
|
'@typescript-eslint/consistent-type-imports': [
|
|
'error',
|
|
{ disallowTypeAnnotations: false },
|
|
],
|
|
'@typescript-eslint/no-explicit-any': 'error',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{
|
|
argsIgnorePattern: '^_',
|
|
varsIgnorePattern: '^_',
|
|
caughtErrorsIgnorePattern: '^_',
|
|
},
|
|
],
|
|
'no-console': ['error', { allow: ['warn', 'error'] }],
|
|
'no-debugger': 'error',
|
|
'object-shorthand': 'error',
|
|
'prefer-const': ['error', { destructuring: 'all' }],
|
|
},
|
|
},
|
|
{
|
|
files: ['packages/web-shell/client/daemon/**/*.{ts,tsx}'],
|
|
rules: {
|
|
'no-console': ['error', { allow: ['debug', 'warn', 'error'] }],
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
'packages/web-shell/client/**/*.test.{ts,tsx}',
|
|
'packages/web-shell/client/test/**/*.{ts,tsx}',
|
|
],
|
|
plugins: {
|
|
vitest,
|
|
},
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.browser,
|
|
...globals.es2021,
|
|
...globals.vitest,
|
|
},
|
|
},
|
|
rules: {
|
|
...vitest.configs.recommended.rules,
|
|
'vitest/expect-expect': 'off',
|
|
'vitest/no-commented-out-tests': 'off',
|
|
'no-console': 'off',
|
|
},
|
|
},
|
|
{
|
|
files: ['packages/web-shell/client/e2e/**/*.{ts,tsx}'],
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.browser,
|
|
...globals.es2021,
|
|
...globals.node,
|
|
},
|
|
},
|
|
rules: {
|
|
'no-console': 'off',
|
|
},
|
|
},
|
|
{
|
|
files: ['packages/core/src/**/*.ts'],
|
|
ignores: [
|
|
'packages/core/src/config/config.ts',
|
|
'**/*.test.ts',
|
|
'**/*.spec.ts',
|
|
'**/__tests__/**',
|
|
'**/generated/**',
|
|
'**/*.generated.ts',
|
|
],
|
|
plugins: {
|
|
'qwen-code': {
|
|
rules: {
|
|
'no-config-object-create': noConfigObjectCreate,
|
|
},
|
|
},
|
|
},
|
|
rules: {
|
|
'qwen-code/no-config-object-create': 'error',
|
|
},
|
|
},
|
|
{
|
|
// Enforce kebab-case filenames
|
|
files: ['packages/core/src/**/*.ts', 'packages/cli/src/**/*.ts'],
|
|
ignores: legacyFilenames.flatMap((name) => [
|
|
`**/${name}.ts`,
|
|
`**/${name}.*.ts`,
|
|
]),
|
|
plugins: {
|
|
'check-file': checkFile,
|
|
},
|
|
rules: {
|
|
'check-file/filename-naming-convention': [
|
|
'error',
|
|
{ '**/*.ts': 'KEBAB_CASE' },
|
|
{ ignoreMiddleExtensions: true },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
files: [
|
|
'packages/*/src/**/*.test.{ts,tsx}',
|
|
'packages/**/test/**/*.test.{ts,tsx}',
|
|
'integrations/**/src/**/*.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',
|
|
'./scripts/**/*.mjs',
|
|
'esbuild.config.js',
|
|
'packages/*/scripts/**/*.js',
|
|
'packages/*/scripts/**/*.mjs',
|
|
'packages/*/build.mjs',
|
|
// web-templates' export-html template build scripts also run with `node`.
|
|
'packages/*/src/export-html/*.mjs',
|
|
// Verification reproducer scripts under docs/ also run with `node`.
|
|
'docs/**/*.mjs',
|
|
// Plan C CDP-tunnel acceptance harness (issue #5626) runs with `node`.
|
|
'packages/cli/src/serve/cdp-tunnel/acceptance/**/*.mjs',
|
|
// Desktop skill helper scripts also run with `node`.
|
|
'packages/desktop/.agents/skills/**/scripts/**/*.mjs',
|
|
],
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.node,
|
|
...globals.browser,
|
|
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',
|
|
},
|
|
},
|
|
{
|
|
files: ['.github/scripts/**/*.{js,mjs,cjs}'],
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.node,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
files: ['packages/desktop/bootstrap/**/*.js'],
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.browser,
|
|
},
|
|
},
|
|
},
|
|
|
|
// The VS Code companion renders through @qwen-code/web-shell; the legacy
|
|
// @qwen-code/webui surface must not re-enter the extension bundle.
|
|
{
|
|
files: ['packages/vscode-ide-companion/src/**/*.{ts,tsx}'],
|
|
rules: {
|
|
'no-restricted-imports': [
|
|
'error',
|
|
{
|
|
patterns: [
|
|
{
|
|
group: ['@qwen-code/webui', '@qwen-code/webui/*'],
|
|
message:
|
|
'vscode-ide-companion must render through @qwen-code/web-shell; do not re-introduce @qwen-code/webui.',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
|
|
// ==================== 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' },
|
|
},
|
|
// Chrome extension (chrome-extension) - the MV3 background service
|
|
// worker and content scripts run in the browser with no stdio; console is
|
|
// the only logging / debugging channel available there.
|
|
{
|
|
files: ['packages/chrome-extension/**/*.ts', 'packages/chrome-extension/**/*.tsx'],
|
|
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',
|
|
},
|
|
},
|
|
);
|