You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
3.9 KiB
114 lines
3.9 KiB
const js = require('@eslint/js');
|
|
const tseslint = require('typescript-eslint');
|
|
const react = require('eslint-plugin-react');
|
|
const reactHooks = require('eslint-plugin-react-hooks');
|
|
const globals = require('globals');
|
|
|
|
/**
|
|
* 自定义规则:禁止在组件/业务代码内写死颜色字面量。
|
|
* 对齐 AGENTS.md 配色禁令——颜色必须统一从 src/theme/colors.ts 导入。
|
|
*
|
|
* 覆盖两类写法:
|
|
* · 十六进制:#RGB / #RGBA / #RRGGBB / #RRGGBBAA
|
|
* · 函数式:rgb() / rgba() / hsl() / hsla()
|
|
* 后者是补上的**盲区**:规则原先只匹配纯 hex,于是 `rgba(0, 0, 0, 0.4)` 这类写死颜色
|
|
* 一路溜过门槛,最终表现为「同一个弹层遮罩在两个页面是两个透明度」——token 明明就在
|
|
* `colors.scrim`,全仓 9 处在用,唯独那一处自己重新推导了一遍(见 4.14)。
|
|
* 同时覆盖模板字面量,避免用 `` `rgba(0,0,0,${a})` `` 绕过去。
|
|
* 例外:theme/colors.ts 本身是设计令牌唯一来源,豁免。
|
|
*/
|
|
const noHardcodedHexColor = {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description:
|
|
'禁止写死颜色(hex / rgb / rgba / hsl),统一从 src/theme/colors.ts 导入',
|
|
},
|
|
schema: [],
|
|
messages: {
|
|
noHex: '禁止写死颜色字面量 "{{value}}",请从 src/theme/colors.ts 导入颜色 token。',
|
|
},
|
|
},
|
|
create(context) {
|
|
const hexRe = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
const fnRe = /(^|[\s,:;(])(rgb|rgba|hsl|hsla)\(/i;
|
|
const isHardcodedColor = (value) => hexRe.test(value) || fnRe.test(value);
|
|
return {
|
|
Literal(node) {
|
|
if (typeof node.value === 'string' && isHardcodedColor(node.value)) {
|
|
context.report({ node, messageId: 'noHex', data: { value: node.value } });
|
|
}
|
|
},
|
|
TemplateElement(node) {
|
|
// 模板字面量的静态片段(`rgba(0,0,0,${a})` 的 raw 是 "rgba(0,0,0,")
|
|
const raw = node.value.raw;
|
|
if (typeof raw === 'string' && isHardcodedColor(raw)) {
|
|
context.report({ node, messageId: 'noHex', data: { value: raw } });
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
module.exports = tseslint.config(
|
|
{
|
|
ignores: [
|
|
'node_modules/**',
|
|
'.expo/**',
|
|
'dist/**',
|
|
'coverage/**',
|
|
'figma_out/**',
|
|
'**/*.mjs',
|
|
'figma_discover.py',
|
|
'**/__tests__/**',
|
|
],
|
|
},
|
|
js.configs.recommended,
|
|
...tseslint.configs.recommended,
|
|
{
|
|
files: ['**/*.{ts,tsx}'],
|
|
plugins: {
|
|
react,
|
|
'react-hooks': reactHooks,
|
|
local: { rules: { 'no-hardcoded-hex-color': noHardcodedHexColor } },
|
|
},
|
|
languageOptions: {
|
|
parser: tseslint.parser,
|
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
sourceType: 'module',
|
|
globals: { ...globals.node, ...globals.react },
|
|
},
|
|
settings: { react: { version: 'detect' } },
|
|
rules: {
|
|
...react.configs.recommended.rules,
|
|
...reactHooks.configs.recommended.rules,
|
|
'react/react-in-jsx-scope': 'off',
|
|
'react/jsx-uses-react': 'off',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
],
|
|
'@typescript-eslint/no-explicit-any': 'warn',
|
|
'no-empty': ['error', { allowEmptyCatch: true }],
|
|
'local/no-hardcoded-hex-color': 'error',
|
|
},
|
|
},
|
|
{
|
|
// 项目配置文件为 CommonJS,含 require / module / process
|
|
files: ['eslint.config.js', 'babel.config.js', 'jest.config.js', 'app.config.js', '*.cjs'],
|
|
languageOptions: {
|
|
sourceType: 'commonjs',
|
|
globals: { ...globals.node },
|
|
},
|
|
rules: {
|
|
'no-undef': 'off',
|
|
'@typescript-eslint/no-require-imports': 'off',
|
|
'@typescript-eslint/no-explicit-any': 'off',
|
|
},
|
|
},
|
|
{
|
|
// 设计令牌文件是颜色唯一来源,豁免 hex 检查
|
|
files: ['**/theme/**/*.ts'],
|
|
rules: { 'local/no-hardcoded-hex-color': 'off' },
|
|
},
|
|
);
|
|
|