// @ts-check import markdown from '@eslint/markdown' import md from 'eslint-markdown' import hyoban from 'eslint-plugin-hyoban' import jsonc from 'eslint-plugin-jsonc' import markdownPreferences from 'eslint-plugin-markdown-preferences' import pnpm from 'eslint-plugin-pnpm' import toml from 'eslint-plugin-toml' import yml from 'eslint-plugin-yml' import { defineConfig, globalIgnores } from 'eslint/config' import dify from './web/plugins/eslint/index.js' const codeFiles = '**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}' const mdPlugin = /** @type {import('eslint').ESLint.Plugin} */ (/** @type {unknown} */ (md)) /** * Migration tradeoff: ESLint is intentionally restricted to non-code files. * * Disabled code-only checks: * - Core: `no-control-regex`, `no-octal`, `no-octal-escape`, `no-undef-init`, * `no-unreachable-loop`, and `one-var`. * - JavaScript: `dot-notation`. * - Declarations: the former 223-rule snapshot and CLI `no-restricted-imports` override. * - Dify UI: the three `better-tailwindcss` rules and stricter unused-directive severity. * * These checks are documentation only and do not appear in the executable config below. */ const tsconfigCompilerOptionsOrder = [ 'incremental', 'composite', 'tsBuildInfoFile', 'disableSourceOfProjectReferenceRedirect', 'disableSolutionSearching', 'disableReferencedProjectLoad', 'target', 'jsx', 'jsxFactory', 'jsxFragmentFactory', 'jsxImportSource', 'lib', 'moduleDetection', 'noLib', 'reactNamespace', 'useDefineForClassFields', 'emitDecoratorMetadata', 'experimentalDecorators', 'libReplacement', 'baseUrl', 'rootDir', 'rootDirs', 'customConditions', 'module', 'moduleResolution', 'moduleSuffixes', 'noResolve', 'paths', 'resolveJsonModule', 'resolvePackageJsonExports', 'resolvePackageJsonImports', 'typeRoots', 'types', 'allowArbitraryExtensions', 'allowImportingTsExtensions', 'allowUmdGlobalAccess', 'allowJs', 'checkJs', 'maxNodeModuleJsDepth', 'strict', 'strictBindCallApply', 'strictFunctionTypes', 'strictNullChecks', 'strictPropertyInitialization', 'allowUnreachableCode', 'allowUnusedLabels', 'alwaysStrict', 'exactOptionalPropertyTypes', 'noFallthroughCasesInSwitch', 'noImplicitAny', 'noImplicitOverride', 'noImplicitReturns', 'noImplicitThis', 'noPropertyAccessFromIndexSignature', 'noUncheckedIndexedAccess', 'noUnusedLocals', 'noUnusedParameters', 'useUnknownInCatchVariables', 'declaration', 'declarationDir', 'declarationMap', 'downlevelIteration', 'emitBOM', 'emitDeclarationOnly', 'importHelpers', 'importsNotUsedAsValues', 'inlineSourceMap', 'inlineSources', 'mapRoot', 'newLine', 'noEmit', 'noEmitHelpers', 'noEmitOnError', 'outDir', 'outFile', 'preserveConstEnums', 'preserveValueImports', 'removeComments', 'sourceMap', 'sourceRoot', 'stripInternal', 'allowSyntheticDefaultImports', 'esModuleInterop', 'forceConsistentCasingInFileNames', 'isolatedDeclarations', 'isolatedModules', 'preserveSymlinks', 'verbatimModuleSyntax', 'erasableSyntaxOnly', 'skipDefaultLibCheck', 'skipLibCheck', ] const pnpmWorkspaceOrder = [ 'cacheDir', 'catalogMode', 'cleanupUnusedCatalogs', 'dedupeDirectDeps', 'deployAllFiles', 'enablePrePostScripts', 'engineStrict', 'extendNodePath', 'hoist', 'hoistPattern', 'hoistWorkspacePackages', 'ignoreCompatibilityDb', 'ignoreDepScripts', 'ignoreScripts', 'ignoreWorkspaceRootCheck', 'managePackageManagerVersions', 'minimumReleaseAge', 'minimumReleaseAgeExclude', 'modulesDir', 'nodeLinker', 'nodeVersion', 'optimisticRepeatInstall', 'packageManagerStrict', 'packageManagerStrictVersion', 'preferSymlinkedExecutables', 'preferWorkspacePackages', 'publicHoistPattern', 'registrySupportsTimeField', 'requiredScripts', 'resolutionMode', 'savePrefix', 'scriptShell', 'shamefullyHoist', 'shellEmulator', 'stateDir', 'supportedArchitectures', 'symlink', 'tag', 'trustPolicy', 'trustPolicyExclude', 'updateNotifier', 'packages', 'overrides', 'patchedDependencies', 'catalog', 'catalogs', 'allowedDeprecatedVersions', 'allowNonAppliedPatches', 'configDependencies', 'ignoredBuiltDependencies', 'ignoredOptionalDependencies', 'neverBuiltDependencies', 'onlyBuiltDependencies', 'onlyBuiltDependenciesFile', 'packageExtensions', 'peerDependencyRules', ] export default defineConfig([ globalIgnores( [ '**/*', '!cli/', '!cli/**/*', '!e2e/', '!e2e/**/*', '!packages/', '!packages/**/*', '!sdks/', '!sdks/nodejs-client/', '!sdks/nodejs-client/src/', '!sdks/nodejs-client/src/**/*', '!sdks/nodejs-client/tests/', '!sdks/nodejs-client/tests/**/*', '!web/', '!web/**/*', '!eslint.config.mjs', '!lint.config.ts', '!package.json', '!pnpm-workspace.yaml', '!vite.config.ts', ], 'Project lint scope', ), globalIgnores([codeFiles], 'Migration tradeoff: code files are handled by Oxlint only'), globalIgnores( [ '**/.git/**', '**/node_modules/**', 'cli/context/**', 'cli/coverage/**', 'cli/dist/**', 'cli/docs/**', '**/.next/**', '**/.vinext/**', '**/coverage/**', '**/dist/**', '**/storybook-static/**', 'e2e/.auth/**', 'e2e/cucumber-report/**', 'packages/contracts/**', 'web/next/**', 'web/next-env.d.ts', 'web/public/**', 'web/types/doc-paths.ts', ], 'Generated and external files', ), { linterOptions: { reportUnusedDisableDirectives: 'warn', }, }, { files: ['**/*.{json,json5,jsonc}'], language: 'jsonc/x', plugins: { jsonc, }, rules: { 'no-unused-expressions': 'off', 'no-unused-vars': 'off', strict: 'off', 'jsonc/no-bigint-literals': 'error', 'jsonc/no-binary-expression': 'error', 'jsonc/no-binary-numeric-literals': 'error', 'jsonc/no-dupe-keys': 'error', 'jsonc/no-escape-sequence-in-identifier': 'error', 'jsonc/no-floating-decimal': 'error', 'jsonc/no-hexadecimal-numeric-literals': 'error', 'jsonc/no-infinity': 'error', 'jsonc/no-multi-str': 'error', 'jsonc/no-nan': 'error', 'jsonc/no-number-props': 'error', 'jsonc/no-numeric-separators': 'error', 'jsonc/no-octal': 'error', 'jsonc/no-octal-escape': 'error', 'jsonc/no-octal-numeric-literals': 'error', 'jsonc/no-parenthesized': 'error', 'jsonc/no-plus-sign': 'error', 'jsonc/no-regexp-literals': 'error', 'jsonc/no-sparse-arrays': 'error', 'jsonc/no-template-literals': 'error', 'jsonc/no-undefined-value': 'error', 'jsonc/no-unicode-codepoint-escapes': 'error', 'jsonc/no-useless-escape': 'error', 'jsonc/valid-json-number': 'error', 'jsonc/vue-custom-block/no-parsing-error': 'error', }, }, { files: ['**/package.json'], rules: { 'jsonc/sort-array-values': [ 'error', { order: { type: 'asc' }, pathPattern: '^files$', }, ], }, }, { files: ['**/[jt]sconfig.json', '**/[jt]sconfig.*.json'], rules: { 'jsonc/sort-keys': [ 'error', { order: ['extends', 'compilerOptions', 'references', 'files', 'include', 'exclude'], pathPattern: '^$', }, { order: tsconfigCompilerOptionsOrder, pathPattern: '^compilerOptions$', }, ], }, }, { files: ['package.json', '**/package.json'], plugins: { pnpm, }, rules: { 'pnpm/json-enforce-catalog': [ 'error', { autofix: true, ignores: ['@types/vscode'], }, ], 'pnpm/json-prefer-workspace-settings': ['error', { autofix: true }], 'pnpm/json-valid-catalog': ['error', { autofix: true }], }, }, { files: ['**/*.{yml,yaml}'], language: 'yml/yaml', plugins: { yml, }, rules: { 'no-irregular-whitespace': 'off', 'no-unused-vars': 'off', 'spaced-comment': 'off', 'yml/no-empty-key': 'error', 'yml/no-empty-sequence-entry': 'error', 'yml/no-irregular-whitespace': 'error', 'yml/vue-custom-block/no-parsing-error': 'error', }, }, { files: ['pnpm-workspace.yaml'], plugins: { pnpm, }, rules: { 'pnpm/yaml-enforce-settings': [ 'error', { settings: { shellEmulator: true, trustPolicy: 'no-downgrade', }, }, ], 'pnpm/yaml-no-duplicate-catalog-item': 'error', 'pnpm/yaml-no-unused-catalog-item': 'error', 'yml/sort-keys': [ 'error', { order: pnpmWorkspaceOrder, pathPattern: '^$', }, { order: { type: 'asc' }, pathPattern: '.*', }, ], }, }, { files: ['**/*.toml'], language: 'toml/toml', plugins: { toml, }, rules: { 'no-irregular-whitespace': 'off', 'spaced-comment': 'off', 'toml/keys-order': 'error', 'toml/no-unreadable-number-separator': 'error', 'toml/precision-of-fractional-seconds': 'error', 'toml/precision-of-integer': 'error', 'toml/tables-order': 'error', 'toml/vue-custom-block/no-parsing-error': 'error', }, }, { files: ['**/*.md'], language: 'markdown/gfm', plugins: { markdown, 'markdown-preferences': markdownPreferences, md: mdPlugin, }, rules: { 'markdown/fenced-code-language': 'off', 'markdown/heading-increment': 'error', 'markdown/no-duplicate-definitions': 'error', 'markdown/no-empty-definitions': 'error', 'markdown/no-empty-images': 'error', 'markdown/no-empty-links': 'error', 'markdown/no-invalid-label-refs': 'error', 'markdown/no-missing-atx-heading-space': 'error', 'markdown/no-missing-label-refs': 'off', 'markdown/no-missing-link-fragments': 'error', 'markdown/no-multiple-h1': 'error', 'markdown/no-reference-like-urls': 'error', 'markdown/no-reversed-media-syntax': 'error', 'markdown/no-space-in-emphasis': 'error', 'markdown/no-unused-definitions': 'error', 'markdown/require-alt-text': 'error', 'markdown/table-column-count': 'error', 'markdown-preferences/definitions-last': 'error', 'markdown-preferences/prefer-link-reference-definitions': [ 'error', { minLinks: 1, }, ], 'markdown-preferences/sort-definitions': 'error', 'md/no-url-trailing-slash': 'error', }, }, { files: ['web/i18n/**/*.json'], plugins: { dify, hyoban, }, rules: { 'dify/consistent-placeholders': 'error', 'dify/no-extra-keys': 'error', 'hyoban/i18n-flat-key': 'error', 'jsonc/sort-keys': 'error', }, }, ])