mirror of https://github.com/langgenius/dify.git
chore: update knip config and include in CI (#30410)
This commit is contained in:
parent
27be89c984
commit
e856287b65
|
|
@ -110,6 +110,11 @@ jobs:
|
|||
working-directory: ./web
|
||||
run: pnpm run type-check:tsgo
|
||||
|
||||
- name: Web dead code check
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
working-directory: ./web
|
||||
run: pnpm run knip
|
||||
|
||||
superlinter:
|
||||
name: SuperLinter
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@ export default {
|
|||
const options = context.options[0] || {}
|
||||
const mode = options.mode || 'any'
|
||||
|
||||
/**
|
||||
* Check if this is a t() function call
|
||||
* @param {import('estree').CallExpression} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isTCall(node) {
|
||||
// Direct t() call
|
||||
if (node.callee.type === 'Identifier' && node.callee.name === 't')
|
||||
|
|
|
|||
|
|
@ -19,26 +19,11 @@ export default {
|
|||
create(context) {
|
||||
const sourceCode = context.sourceCode
|
||||
|
||||
// Track all t() calls to fix
|
||||
/** @type {Array<{ node: import('estree').CallExpression }>} */
|
||||
const tCallsToFix = []
|
||||
|
||||
// Track variables with namespace prefix
|
||||
/** @type {Map<string, { node: import('estree').VariableDeclarator, name: string, oldValue: string, newValue: string, ns: string }>} */
|
||||
const variablesToFix = new Map()
|
||||
|
||||
// Track all namespaces used in the file (from legacy prefix detection)
|
||||
/** @type {Set<string>} */
|
||||
const namespacesUsed = new Set()
|
||||
|
||||
// Track variable values for template literal analysis
|
||||
/** @type {Map<string, string>} */
|
||||
const variableValues = new Map()
|
||||
|
||||
/**
|
||||
* Analyze a template literal and extract namespace info
|
||||
* @param {import('estree').TemplateLiteral} node
|
||||
*/
|
||||
function analyzeTemplateLiteral(node) {
|
||||
const quasis = node.quasis
|
||||
const expressions = node.expressions
|
||||
|
|
@ -78,11 +63,6 @@ export default {
|
|||
return { ns: null, canFix: false, fixedQuasis: null, variableToUpdate: null }
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a fixed template literal string
|
||||
* @param {string[]} quasis
|
||||
* @param {import('estree').Expression[]} expressions
|
||||
*/
|
||||
function buildTemplateLiteral(quasis, expressions) {
|
||||
let result = '`'
|
||||
for (let i = 0; i < quasis.length; i++) {
|
||||
|
|
@ -95,11 +75,6 @@ export default {
|
|||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a t() call already has ns in its second argument
|
||||
* @param {import('estree').CallExpression} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasNsArgument(node) {
|
||||
if (node.arguments.length < 2)
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -12,11 +12,6 @@ export default {
|
|||
},
|
||||
},
|
||||
create(context) {
|
||||
/**
|
||||
* Check if a t() call has ns in its second argument
|
||||
* @param {import('estree').CallExpression} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasNsOption(node) {
|
||||
if (node.arguments.length < 2)
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -1,277 +1,33 @@
|
|||
import type { KnipConfig } from 'knip'
|
||||
|
||||
/**
|
||||
* Knip Configuration for Dead Code Detection
|
||||
*
|
||||
* This configuration helps identify unused files, exports, and dependencies
|
||||
* in the Dify web application (Next.js 15 + TypeScript + React 19).
|
||||
*
|
||||
* ⚠️ SAFETY FIRST: This configuration is designed to be conservative and
|
||||
* avoid false positives that could lead to deleting actively used code.
|
||||
*
|
||||
* @see https://knip.dev/reference/configuration
|
||||
*/
|
||||
const config: KnipConfig = {
|
||||
// ============================================================================
|
||||
// Next.js Framework Configuration
|
||||
// ============================================================================
|
||||
// Configure entry points specific to Next.js application structure.
|
||||
// These files are automatically treated as entry points by the framework.
|
||||
next: {
|
||||
entry: [
|
||||
// Next.js App Router pages (must exist for routing)
|
||||
'app/**/page.tsx',
|
||||
'app/**/layout.tsx',
|
||||
'app/**/loading.tsx',
|
||||
'app/**/error.tsx',
|
||||
'app/**/not-found.tsx',
|
||||
'app/**/template.tsx',
|
||||
'app/**/default.tsx',
|
||||
|
||||
// Middleware (runs before every route)
|
||||
'middleware.ts',
|
||||
|
||||
// Configuration files
|
||||
'next.config.js',
|
||||
'tailwind.config.js',
|
||||
'tailwind-common-config.ts',
|
||||
'postcss.config.js',
|
||||
|
||||
// Linting configuration
|
||||
'eslint.config.mjs',
|
||||
],
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
// Global Entry Points
|
||||
// ============================================================================
|
||||
// Files that serve as entry points for the application.
|
||||
// The '!' suffix means these patterns take precedence and are always included.
|
||||
entry: [
|
||||
// Next.js App Router patterns (high priority)
|
||||
'app/**/page.tsx!',
|
||||
'app/**/layout.tsx!',
|
||||
'app/**/loading.tsx!',
|
||||
'app/**/error.tsx!',
|
||||
'app/**/not-found.tsx!',
|
||||
'app/**/template.tsx!',
|
||||
'app/**/default.tsx!',
|
||||
|
||||
// Core configuration files
|
||||
'middleware.ts!',
|
||||
'next.config.js!',
|
||||
'tailwind.config.js!',
|
||||
'tailwind-common-config.ts!',
|
||||
'postcss.config.js!',
|
||||
|
||||
// Linting setup
|
||||
'eslint.config.mjs!',
|
||||
|
||||
// ========================================================================
|
||||
// 🔒 CRITICAL: Global Initializers and Providers
|
||||
// ========================================================================
|
||||
// These files are imported by root layout.tsx and provide global functionality.
|
||||
// Even if not directly imported elsewhere, they are essential for app initialization.
|
||||
|
||||
// Browser initialization (runs on client startup)
|
||||
'app/components/browser-initializer.tsx!',
|
||||
'app/components/sentry-initializer.tsx!',
|
||||
'app/components/app-initializer.tsx!',
|
||||
|
||||
// i18n initialization (server and client)
|
||||
'app/components/i18n.tsx!',
|
||||
'app/components/i18n-server.tsx!',
|
||||
|
||||
// Route prefix handling (used in root layout)
|
||||
'app/routePrefixHandle.tsx!',
|
||||
|
||||
// ========================================================================
|
||||
// 🔒 CRITICAL: Context Providers
|
||||
// ========================================================================
|
||||
// Context providers might be used via React Context API and imported dynamically.
|
||||
// Protecting all context files to prevent breaking the provider chain.
|
||||
'context/**/*.ts?(x)!',
|
||||
|
||||
// Component-level contexts (also used via React.useContext)
|
||||
'app/components/**/*.context.ts?(x)!',
|
||||
|
||||
// ========================================================================
|
||||
// 🔒 CRITICAL: State Management Stores
|
||||
// ========================================================================
|
||||
// Zustand stores might be imported dynamically or via hooks.
|
||||
// These are often imported at module level, so they should be protected.
|
||||
'app/components/**/*.store.ts?(x)!',
|
||||
'context/**/*.store.ts?(x)!',
|
||||
|
||||
// ========================================================================
|
||||
// 🔒 CRITICAL: Provider Components
|
||||
// ========================================================================
|
||||
// Provider components wrap the app and provide global state/functionality
|
||||
'app/components/**/*.provider.ts?(x)!',
|
||||
'context/**/*.provider.ts?(x)!',
|
||||
|
||||
// ========================================================================
|
||||
// Development tools
|
||||
// ========================================================================
|
||||
// Storybook configuration
|
||||
'.storybook/**/*',
|
||||
'scripts/**/*.{js,ts,mjs}',
|
||||
'bin/**/*.{js,ts,mjs}',
|
||||
],
|
||||
|
||||
// ============================================================================
|
||||
// Project Files to Analyze
|
||||
// ============================================================================
|
||||
// Glob patterns for files that should be analyzed for unused code.
|
||||
// Excludes test files to avoid false positives.
|
||||
project: [
|
||||
'**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
||||
],
|
||||
|
||||
// ============================================================================
|
||||
// Ignored Files and Directories
|
||||
// ============================================================================
|
||||
// Files and directories that should be completely excluded from analysis.
|
||||
// These typically contain:
|
||||
// - Test files
|
||||
// - Internationalization files (loaded dynamically)
|
||||
// - Static assets
|
||||
// - Build outputs
|
||||
// - External libraries
|
||||
ignore: [
|
||||
// Test files and directories
|
||||
'**/__tests__/**',
|
||||
'**/*.spec.{ts,tsx}',
|
||||
'**/*.test.{ts,tsx}',
|
||||
|
||||
// ========================================================================
|
||||
// 🔒 CRITICAL: i18n Files (Dynamically Loaded)
|
||||
// ========================================================================
|
||||
// Internationalization files are loaded dynamically at runtime via i18next.
|
||||
// Pattern: import(`@/i18n/${locale}/messages`)
|
||||
// These will NEVER show up in static analysis but are essential!
|
||||
'i18n/**',
|
||||
|
||||
// ========================================================================
|
||||
// 🔒 CRITICAL: Static Assets
|
||||
// ========================================================================
|
||||
// Static assets are referenced by URL in the browser, not via imports.
|
||||
// Examples: /logo.png, /icons/*, /embed.js
|
||||
'public/**',
|
||||
|
||||
// Build outputs and caches
|
||||
'node_modules/**',
|
||||
'.next/**',
|
||||
'coverage/**',
|
||||
|
||||
// Development tools
|
||||
'**/*.stories.{ts,tsx}',
|
||||
|
||||
// ========================================================================
|
||||
// 🔒 Utility scripts (not part of application runtime)
|
||||
// ========================================================================
|
||||
// These scripts are run manually (e.g., pnpm gen-icons, pnpm i18n:check)
|
||||
// and are not imported by the application code.
|
||||
'scripts/**',
|
||||
'bin/**',
|
||||
'i18n-config/**',
|
||||
|
||||
// Icon generation script (generates components, not used in runtime)
|
||||
'app/components/base/icons/script.mjs',
|
||||
],
|
||||
|
||||
// ============================================================================
|
||||
// Ignored Dependencies
|
||||
// ============================================================================
|
||||
// Dependencies that are used but not directly imported in code.
|
||||
// These are typically:
|
||||
// - Build tools
|
||||
// - Plugins loaded by configuration files
|
||||
// - CLI tools
|
||||
ignoreDependencies: [
|
||||
// ========================================================================
|
||||
// Next.js plugins (loaded by next.config.js)
|
||||
// ========================================================================
|
||||
'next-pwa',
|
||||
'@next/bundle-analyzer',
|
||||
'@next/mdx',
|
||||
|
||||
// ========================================================================
|
||||
// Build tools (used by webpack/next.js build process)
|
||||
// ========================================================================
|
||||
'code-inspector-plugin',
|
||||
|
||||
// ========================================================================
|
||||
// Development and translation tools (used by scripts)
|
||||
// ========================================================================
|
||||
'bing-translate-api',
|
||||
'uglify-js',
|
||||
],
|
||||
|
||||
// ============================================================================
|
||||
// Export Analysis Configuration
|
||||
// ============================================================================
|
||||
// Configure how exports are analyzed
|
||||
|
||||
// Ignore exports that are only used within the same file
|
||||
// (e.g., helper functions used internally in the same module)
|
||||
ignoreExportsUsedInFile: true,
|
||||
|
||||
// ⚠️ SAFETY: Include exports from entry files in the analysis
|
||||
// This helps find unused public APIs, but be careful with:
|
||||
// - Context exports (useContext hooks)
|
||||
// - Store exports (useStore hooks)
|
||||
// - Type exports (might be used in other files)
|
||||
includeEntryExports: true,
|
||||
|
||||
// ============================================================================
|
||||
// Ignored Binaries
|
||||
// ============================================================================
|
||||
// Binary executables that are used but not listed in package.json
|
||||
ignoreBinaries: [
|
||||
'only-allow', // Used in preinstall script to enforce pnpm usage
|
||||
'only-allow',
|
||||
],
|
||||
|
||||
// ============================================================================
|
||||
// Reporting Rules
|
||||
// ============================================================================
|
||||
// Configure what types of issues to report and at what severity level
|
||||
rules: {
|
||||
// ========================================================================
|
||||
// Unused files are ERRORS
|
||||
// ========================================================================
|
||||
// These should definitely be removed or used.
|
||||
// However, always manually verify before deleting!
|
||||
files: 'error',
|
||||
|
||||
// ========================================================================
|
||||
// Unused dependencies are WARNINGS
|
||||
// ========================================================================
|
||||
// Dependencies might be:
|
||||
// - Used in production builds but not in dev
|
||||
// - Peer dependencies
|
||||
// - Used by other tools
|
||||
files: 'warn',
|
||||
dependencies: 'warn',
|
||||
devDependencies: 'warn',
|
||||
|
||||
// ========================================================================
|
||||
// Unlisted imports are ERRORS
|
||||
// ========================================================================
|
||||
// Missing from package.json - will break in production!
|
||||
unlisted: 'error',
|
||||
|
||||
// ========================================================================
|
||||
// Unused exports are WARNINGS (not errors!)
|
||||
// ========================================================================
|
||||
// Exports might be:
|
||||
// - Part of public API for future use
|
||||
// - Used by external tools
|
||||
// - Exported for type inference
|
||||
// ⚠️ ALWAYS manually verify before removing exports!
|
||||
optionalPeerDependencies: 'warn',
|
||||
unlisted: 'warn',
|
||||
unresolved: 'warn',
|
||||
exports: 'warn',
|
||||
|
||||
// Unused types are warnings (might be part of type definitions)
|
||||
nsExports: 'warn',
|
||||
classMembers: 'warn',
|
||||
types: 'warn',
|
||||
|
||||
// Duplicate exports are warnings (could cause confusion but not breaking)
|
||||
nsTypes: 'warn',
|
||||
enumMembers: 'warn',
|
||||
duplicates: 'warn',
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
"type-check": "tsc --noEmit",
|
||||
"type-check:tsgo": "tsgo --noEmit",
|
||||
"prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky",
|
||||
"gen-icons": "node ./app/components/base/icons/script.mjs",
|
||||
"gen-icons": "node ./scripts/gen-icons.mjs && eslint --fix app/components/base/icons/src/",
|
||||
"uglify-embed": "node ./bin/uglify-embed",
|
||||
"i18n:check": "tsx ./scripts/check-i18n.js",
|
||||
"i18n:gen": "tsx ./scripts/auto-gen-i18n.js",
|
||||
|
|
@ -190,7 +190,6 @@
|
|||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"@vitest/coverage-v8": "4.0.16",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-loader": "^10.0.0",
|
||||
"bing-translate-api": "^4.1.0",
|
||||
"code-inspector-plugin": "1.2.9",
|
||||
"cross-env": "^10.1.0",
|
||||
|
|
@ -201,10 +200,9 @@
|
|||
"eslint-plugin-storybook": "^10.1.10",
|
||||
"eslint-plugin-tailwindcss": "^3.18.2",
|
||||
"husky": "^9.1.7",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"jsdom": "^27.3.0",
|
||||
"jsdom-testing-mocks": "^1.16.0",
|
||||
"knip": "^5.66.1",
|
||||
"knip": "^5.78.0",
|
||||
"lint-staged": "^15.5.2",
|
||||
"nock": "^14.0.10",
|
||||
"postcss": "^8.5.6",
|
||||
|
|
|
|||
|
|
@ -481,9 +481,6 @@ importers:
|
|||
autoprefixer:
|
||||
specifier: ^10.4.21
|
||||
version: 10.4.22(postcss@8.5.6)
|
||||
babel-loader:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
|
||||
bing-translate-api:
|
||||
specifier: ^4.1.0
|
||||
version: 4.2.0
|
||||
|
|
@ -514,9 +511,6 @@ importers:
|
|||
husky:
|
||||
specifier: ^9.1.7
|
||||
version: 9.1.7
|
||||
istanbul-lib-coverage:
|
||||
specifier: ^3.2.2
|
||||
version: 3.2.2
|
||||
jsdom:
|
||||
specifier: ^27.3.0
|
||||
version: 27.3.0(canvas@3.2.0)
|
||||
|
|
@ -524,8 +518,8 @@ importers:
|
|||
specifier: ^1.16.0
|
||||
version: 1.16.0
|
||||
knip:
|
||||
specifier: ^5.66.1
|
||||
version: 5.72.0(@types/node@18.15.0)(typescript@5.9.3)
|
||||
specifier: ^5.78.0
|
||||
version: 5.78.0(@types/node@18.15.0)(typescript@5.9.3)
|
||||
lint-staged:
|
||||
specifier: ^15.5.2
|
||||
version: 15.5.2
|
||||
|
|
@ -4271,13 +4265,6 @@ packages:
|
|||
peerDependencies:
|
||||
postcss: ^8.1.0
|
||||
|
||||
babel-loader@10.0.0:
|
||||
resolution: {integrity: sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==}
|
||||
engines: {node: ^18.20.0 || ^20.10.0 || >=22.0.0}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.12.0
|
||||
webpack: '>=5.61.0'
|
||||
|
||||
babel-loader@8.4.1:
|
||||
resolution: {integrity: sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==}
|
||||
engines: {node: '>= 8.9'}
|
||||
|
|
@ -6336,8 +6323,8 @@ packages:
|
|||
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
knip@5.72.0:
|
||||
resolution: {integrity: sha512-rlyoXI8FcggNtM/QXd/GW0sbsYvNuA/zPXt7bsuVi6kVQogY2PDCr81bPpzNnl0CP8AkFm2Z2plVeL5QQSis2w==}
|
||||
knip@5.78.0:
|
||||
resolution: {integrity: sha512-nB7i/fgiJl7WVxdv5lX4ZPfDt9/zrw/lOgZtyioy988xtFhKuFJCRdHWT1Zg9Avc0yaojvnmEuAXU8SeMblKww==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
|
@ -13093,12 +13080,6 @@ snapshots:
|
|||
postcss: 8.5.6
|
||||
postcss-value-parser: 4.2.0
|
||||
|
||||
babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)):
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
find-up: 5.0.0
|
||||
webpack: 5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)
|
||||
|
||||
babel-loader@8.4.1(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)):
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
|
|
@ -15468,7 +15449,7 @@ snapshots:
|
|||
|
||||
kleur@4.1.5: {}
|
||||
|
||||
knip@5.72.0(@types/node@18.15.0)(typescript@5.9.3):
|
||||
knip@5.78.0(@types/node@18.15.0)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
'@types/node': 18.15.0
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { parseXml } from '@rgrove/parse-xml'
|
|||
import { camelCase, template } from 'es-toolkit/compat'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const iconsDir = path.resolve(__dirname, '../app/components/base/icons')
|
||||
|
||||
const generateDir = async (currentPath) => {
|
||||
try {
|
||||
|
|
@ -32,7 +33,7 @@ const processSvgStructure = (svgStructure, replaceFillOrStrokeColor) => {
|
|||
}
|
||||
}
|
||||
const generateSvgComponent = async (fileHandle, entry, pathList, replaceFillOrStrokeColor) => {
|
||||
const currentPath = path.resolve(__dirname, 'src', ...pathList.slice(2))
|
||||
const currentPath = path.resolve(iconsDir, 'src', ...pathList.slice(2))
|
||||
|
||||
try {
|
||||
await access(currentPath)
|
||||
|
|
@ -86,7 +87,7 @@ export { default as <%= svgName %> } from './<%= svgName %>'
|
|||
}
|
||||
|
||||
const generateImageComponent = async (entry, pathList) => {
|
||||
const currentPath = path.resolve(__dirname, 'src', ...pathList.slice(2))
|
||||
const currentPath = path.resolve(iconsDir, 'src', ...pathList.slice(2))
|
||||
|
||||
try {
|
||||
await access(currentPath)
|
||||
|
|
@ -167,8 +168,8 @@ const walk = async (entry, pathList, replaceFillOrStrokeColor) => {
|
|||
}
|
||||
|
||||
(async () => {
|
||||
await rm(path.resolve(__dirname, 'src'), { recursive: true, force: true })
|
||||
await walk('public', [__dirname, 'assets'])
|
||||
await walk('vender', [__dirname, 'assets'], true)
|
||||
await walk('image', [__dirname, 'assets'])
|
||||
await rm(path.resolve(iconsDir, 'src'), { recursive: true, force: true })
|
||||
await walk('public', [iconsDir, 'assets'])
|
||||
await walk('vender', [iconsDir, 'assets'], true)
|
||||
await walk('image', [iconsDir, 'assets'])
|
||||
})()
|
||||
Loading…
Reference in New Issue