mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-09-11 03:08:00 +00:00
* fix(export): stop inlining the interactive Web Shell runtime in exported HTML /export html builds document-main.tsx with esbuild bundle:true and inlines the result into every exported file. The entry imported WebShellTranscript from the @qwen-code/web-shell package root, whose facade also reaches App, the daemon providers, the editor/terminal chrome and the full injected component stylesheet, so each export carried 19,523,259 runtime bytes (62x the legacy renderer) even for an empty session (#11031). Add a transcript-only subpath entry (@qwen-code/web-shell/transcript, mirroring the existing ./daemon-react-sdk subpath and the SDK's ./daemon/transcript pattern) that exports only the read-only transcript renderer, and point document-main.tsx at it. The entry is built in its own rollup run so it carries only the stylesheet reachable from transcript rendering instead of the whole component sheet. Exports stay fully self-contained: CSP, nonce handling and offline rendering are unchanged. Also add a hard size budget on the inlined document runtime in the template build, mirroring the MAX_*_BROWSER_BUNDLE_BYTES assertions in packages/sdk-typescript/scripts/build.js. The limit sits below the regression value so a repeat fails the build instead of landing silently. Measured on the generated document template (empty session): - before: 19,525,807 bytes raw / 4,775,943 gzip (runtime 19,523,259) - after: 17,966,485 bytes raw / 4,512,650 gzip (runtime 17,963,937) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-issue-patrol/jmtn7fhrmj3 * fix(export): drop dead Shiki and CodeMirror from the export runtime The transcript-only entry stopped the interactive shell from reaching the inlined document runtime, but two large graphs still did, and the size budget could not see either of them. - Shiki (~9.7 MB of pre-minify inputs, the single largest) is unreachable in an export: `CodeBlock` returns before touching the highlighter when `renderMode === 'document'` and always renders a plain `<pre>`. It is a static import of `codeHighlighter.ts`, so esbuild cannot drop it. The document build now resolves `shiki` / `@shikijs/*` to a stub. Its Oniguruma WASM engine could not have started under the export CSP (`script-src 'nonce-…'`, no `'wasm-unsafe-eval'`) in any case. - CodeMirror (~1 MB) still reached the transcript entry: `UserMessage` imported three composer-tag string getters from `hooks/useComposerCore.ts`, which pulls the whole editor at top level. The getters move to `utils/composerTag.ts` (editor-free, and already imported by `UserMessage`); `useComposerCore` re-exports them for existing callers. Replace the byte-cap-only ratchet with a structural one: the document build now emits a metafile and fails when a forbidden module graph appears in the inputs (Shiki, the web-shell package root, CodeMirror), naming the reason. The byte cap stays as a coarse backstop and still needs re-measuring. Also fix a latent collision introduced by building the transcript entry in its own rollup run: both entries injected their stylesheet under the same `data-qwen-web-shell="component"` key, so a host importing both would keep whichever loaded first and silently lose the other's rules. Injection is now keyed per entry, and shadow-root style adoption concatenates every match. Refs #11031 * docs(verification): add the export-html runtime size verification plan The change to the inlined `/export html` runtime rests on measurements that cannot be taken on the machine that wrote it. Rather than leave that as a caveat in the PR description, commit the plan to the branch so any build-capable machine gets it by cloning: exact commands, the reference numbers to compare against and whose machine they came from, the premise the Shiki removal rests on plus a way to falsify it independently, the two budget constants that are knowingly stale, and what to report back. Follows the docs/verification/abort-controller-refactor layout; results belong next to it as results.md. Refs #11031 * test(web-shell): scope the transcript entry guard to its JS graph The not.toContain('vaul') guard ran against the whole bundle, but injectCssModules prepends the Tailwind-generated stylesheet as a single-line __qwenWebShellCss constant, and Tailwind v4 compiles classes from every scanned source file regardless of the entry's module graph — so drawer.tsx's data-[vaul-drawer-direction=…] variant selectors land in the transcript entry's CSS even though no transcript JS imports vaul (verified: 64 'vaul' occurrences in the injected CSS line, 0 in the JS remainder, no from"vaul" import specifier). Strip the injected CSS line before the graph guards. If the injection shape ever changes, the replace() is a no-op and the checks fail loudly rather than passing falsely. * fix(export): drop the dead echarts runtime from the export bundle CI measured the merged branch at 8,456,076 runtime bytes, and named the inputs: `echarts 3841596` and `zrender 624992` are still the largest single block in it. They arrive the same way mermaid does. `@datafe-open/markdown-chart-echarts` ends `createEChartsRenderer` with const loadECharts = options.loadECharts ?? (async () => await import('echarts')); and this repository never takes that default: `MarkdownChartRenderer` always passes a `loadECharts`, because `adaptLegacyRuntimeLoader` returns a function that throws 'Chart runtime is unavailable.' when no `loadEcharts` prop was supplied, and no call site in `packages/web-shell/client/` supplies one. So the fallback is dead in this codebase — but a bundler cannot prove that, and the export build is esbuild `format: 'iife'` with a single outfile, which cannot code-split, so the dynamic import is flattened straight into the renderer. Resolves `echarts` to a stub on exactly the reasoning the Shiki stub already uses in this file — unreachable in document mode — and adds echarts/zrender to FORBIDDEN_DOCUMENT_INPUTS so it cannot come back unnoticed. Chart blocks in an exported transcript already could not render; they threw 'Chart runtime is unavailable.' before this change and do the same after. Whether exports *should* render charts and diagrams is #11091, and this does not prejudge it: giving the renderer a real runtime stays a deliberate act. Also lowers the runtime budget from 19,000,000 to 8,700,000 using the CI measurement above. It is still loose, because the echarts stub in this same commit lowers the real number again — tighten it from the next green run rather than from a guess. The structural guard is the load-bearing check. * fix(web-shell): make the transcript entry's "no daemon providers" claim true Review found the entry docblock over-claiming. `WebShellTranscript.tsx` value-imported `transcriptBlocksToLocalizedMessages` from `hooks/useMessages`, and that module value-imports `useConnection` / `useTranscriptBlocks` / `useWorkspace` from the `daemon-react-sdk` barrel — so `dist/transcript.js` still carried the provider guards, including `useDaemonActions must be used within DaemonSessionProvider`. Gives that helper the same leaf-module treatment as the composer tags: the pure projection (and the `Translator` type it needs) moves to `adapters/localizedMessages.ts`, which imports nothing from the daemon SDK barrel, and `useMessages.ts` re-exports both so existing callers and its own test are unaffected. `build-artifact.test.ts` now asserts `dist/transcript.js` contains neither `DaemonSessionProvider` nor `DaemonWorkspaceProvider`, so the docblock's claim is pinned rather than asserted in prose. Also fixes the read-only recipe in `packages/web-shell/README.md`, which still taught the root import this PR makes wrong — a host following it would ship the interactive shell, which the reviewer measured as landing a document at ~8.8 MB, past the new cap. Same for the example in the read-only transcript design doc. Tightens the runtime budget to 7,400,000 / 7,300,000 warning, from the reviewer's local measurement of 7,275,173 bytes with the echarts stub in place (CI's 8,456,076 predates it). * fix(web-shell): drop the now-unused adapter import from useMessages The projection that used transcriptBlocksToDaemonMessages moved to adapters/localizedMessages.ts in1d94060f5, leaving the import behind. Lint & Static caught it: 14:10 error 'transcriptBlocksToDaemonMessages' is defined but never used * fix(web-shell): assert what the transcript entry delivers, not what it doesn't The guard added in1d94060f5was red against a clean build. Moving `transcriptBlocksToLocalizedMessages` out of `hooks/useMessages` was correct but it was one of several paths into the daemon barrel, and not the load- bearing one: review's static walk of the entry's value-import graph found three more, through components the transcript genuinely renders — `useArtifactWorkspaceTarget.ts` (`useWorkspace`, `useWorkspaceActions`), `McpStatusMessage.tsx` (`useMcp`) and `TasksStatusMessage.tsx` (`useActions`). Every one of those barrel aliases resolves to a strict hook, so the provider guards ship no matter which module the import names. Consistent with that, `dist/transcript.js` came out 90 bytes larger after the extraction, not smaller. Asserting `DaemonSessionProvider` is absent therefore asserted something this entry does not deliver. Replaced with a bound on the transcript JS remainder — 1,140,948 measured bytes against 7,021,715 for `dist/index.js`, ceiling 1,300,000 — which pins the property the entry actually exists for. The entry docblock now says the same thing: no `App`, no composer, no editor/terminal chrome, and the daemon hook runtime named as a known residual with #11100 as its home. That issue also carries the question this raised — `TasksStatusMessage` calls `useActions()` unconditionally and `WebShellTranscript` mounts no provider, so whether an export containing a tasks-status block renders or throws depends on what the document projector filters, and nobody has checked. * fix(web-shell): preserve KaTeX cascade in transcript bundle --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: yiliang114 <jinjing.zzj@gmail.com>
610 lines
18 KiB
JavaScript
610 lines
18 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 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 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.',
|
|
},
|
|
];
|
|
|
|
export default tseslint.config(
|
|
{
|
|
// Global ignores
|
|
ignores: [
|
|
'node_modules/*',
|
|
'packages/**/dist/**',
|
|
'packages/web-templates/src/generated/**',
|
|
'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-shell/runtime/**',
|
|
'packages/desktop-shell/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',
|
|
},
|
|
},
|
|
{
|
|
// 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',
|
|
},
|
|
},
|
|
{
|
|
// 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-shell skill helper scripts also run with `node`.
|
|
'packages/desktop-shell/.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-shell/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',
|
|
},
|
|
},
|
|
);
|