@charcuterie/eslint-config
Biome负责格式化和大部分的林克. 这个包只持有什么 生物体 无法 表达式:需要类型脚本类型的信息的规则,以及一个自定义 AST查询.
从 mux-magic/eslint.config.js,每个公约的参考应用
在此 repo 中, apps 消耗一个导入, 而不是六个复制的配置文件 .
安装
yarn add --dev @charcuterie/eslint-config eslint
两个包裹,和 eslint 只是因为CLI属于
消费者. typescript-eslint, (中文(简体) ), eslint-plugin-react 财务报告和审定财务报表 @vitest/eslint-plugin 为:
这个软件包本身的依赖性——一个共享的配置,其领养指令开始
"先安装这四个插件"的分享不多.
如果你的配置成像的话 tseslint.configs.* 从这里导入 :
import {
createAppConfig,
tseslint,
} from "@charcuterie/eslint-config"
不是从你自己 typescript-eslint平面配置抛出
Cannot redefine plugin "@typescript-eslint" 当两个块注册命名空间时
有两个不同的对象, 和第二个副本 typescript-eslint 在树上
没错. board-game-picker 将预设套装在锁上
而这个包 解决了8.67.0 旁边 eslint . 彻底失败; docket, (中文(简体) ),
使用相同的申报范围,去除并工作。 哪个回扣是财产
它的锁文件, 而非它的配置—— 因此实例被再出口, 有一个导入
其分明者.
使用量
// eslint.config.js — the whole file, in an app repo
import { createAppConfig } from "@charcuterie/eslint-config"
import { defineConfig } from "eslint/config"
export default defineConfig(
...createAppConfig({
tsconfigRootDir: import.meta.dirname,
}),
)
createAppConfig 是应用程序的默认答案。 由下面的工厂组成
按照正确的顺序, 缩小范围 @charcuterie/ui房子已经忽略了列表
已经就位。 如果不是,请写出来源所在 src数字 :
createAppConfig({
tsconfigRootDir: import.meta.dirname,
appDirectories: ["packages/web"],
// Repo-specific paths are *added* to `APP_IGNORES`, not
// swapped for it.
ignores: ["e2e/**"],
})
| 选项 | 默认 | 它会做什么 |
|---|---|---|
tsconfigRootDir | 所需经费 | 应用程序在哪里 tsconfig.json 活着. projectService 没有诚实的违约 别人的回扣. |
appDirectories | ["src"] | 应用程序的自有来源. 这是使组件规则远离图书馆的范围. |
ignores | [] | 附录 APP_IGNORES。 。 。 . |
componentChoice | "pickers" | "all" 所有7个规则,一旦应用程序 扫过它的生锚和按钮; "off" 中期移民. |
flexOverflow | "off" | "warn" 或者说 "error" 以启动弹性家庭. |
reactVersion | "19.0.0" | |
storyFiles | MULTI_COMPONENT_FILE_GLOBS | 可声明多个组件的文件. |
react/no-multi-comp 说些故事, __fixtures__, (中文(简体) ), *.test.tsx图标
模块 (单位:千美元)icons.tsx, (中文(简体) ), *Icons.tsx) (中文(简体) ). 上两个发现 真正的应用程序运行的那一刻
预设, 并且是同样的论点 故事豁免已经做了—— 文件的工作是
保持一组。 需要包装器的组件测试 宣告了旁边的绳子
charcuterie没有故意的图标, 所以每个应用程序都有一个格言文件:
19岁 mail-sifter/components/icons.tsx十九个单行文件更糟糕,
没有人会写,规则就会被关闭.
为什么预设会存在,因为下面的每个工厂都已经
files:因为8个应用程序各自由手组成,到2026-08-21 6
已经漂流。 四个手工注册的插件对象和两个拾取规则
名称——同一段的四份解释为何不打电话
createComponentChoiceRules 旁边是 两个人根本没接过电线
分舰队的一条机器强制采摘规则 在分舰队的一半被执行。 规则
图书馆的船,但每个消费者必须重新思考 是规则舰队不
已经.
componentChoice: "pickers" 是默认,因为它是一个组件选择
家庭有长期决定,没有剩余积压: Select 实值
堕落, Picker 和每个拥有的应用程序被扫描到 Listbox 打开
2026-08-21. (中文(简体) ). 其他五个规则每个名字 一个扫描一个应用还没有完成
no-raw-button 单枪匹马地向舰队的每一个图标行开火
整个回波红色是一个被返回而不是迁移的配置.
flexOverflow 默认为 "off" 出于同样的原因,向前再走一步:
no-shrink-0-with-flex-wrap 是一个 error,所以捆绑它会 使"通过
预设 ” 和“ 擦去弹性错误” 相同的更改.
手工制作
使用预设的应用程序无法描述是否仍然直接调用工厂
因此没有第二个执行程序可以保留
步骤. mux-magic 这样做,因为它 层自己 no-restricted-syntax
选中的逻辑属性 .
// eslint.config.js
import {
createComponentChoiceRules,
createFlexOverflowRules,
createLogicalPropertiesRules,
createPickerRules,
createReactRules,
createStoryOverrides,
createTestRules,
createTypedRules,
} from "@charcuterie/eslint-config"
import { defineConfig } from "eslint/config"
export default defineConfig(
{ ignores: ["**/dist/**", "**/node_modules/**"] },
createTypedRules({
tsconfigRootDir: import.meta.dirname,
}),
createReactRules({ files: ["packages/ui/**/*.tsx"] }),
createLogicalPropertiesRules({
files: ["packages/ui/**/*.tsx"],
}),
// The settled subset — `no-raw-select`,
// `prefer-listbox-over-select`, and the suppression guard.
createPickerRules({
files: ["packages/web/**/*.tsx"],
}),
// All seven. Opt-in, and pointed at app source rather than the
// library.
createComponentChoiceRules({
files: ["packages/web/**/*.tsx"],
}),
// Also opt-in. Warns by default; `severity: "error"` once the
// app has been swept.
createFlexOverflowRules({
files: ["packages/web/**/*.tsx"],
}),
createStoryOverrides({}),
createTestRules({}),
)
每个导出都是 工厂 files 而不是固定的配置数组。 A 类
共享硬码配置 packages/web/** 是个迷幻的装扮
共享名称; 消费者知道自己的布局, 而本套件没有.
createAppConfig 不打破它,它从 appDirectories
而不是假设他们.
createTypedRules 需求 tsconfigRootDir 因为它打开 projectService —— 说
没有诚实的默认 别人的tsconfig生活的地方.
规则
| 规则 | 它为什么在这里而不是在Biome |
|---|---|
@typescript-eslint/naming-convention (是/有布尔斯) | 键关 types: ["boolean"],需要类型检查器 |
id-length (单位2, _ 财务报告和审定财务报表 $ 豁免) | Biome没有等同物 |
react/no-multi-comp | 每个文件一个组件;故事关闭 __fixtures__ |
vitest/consistent-test-it (单位:千美元)test没有 it(中文(简体) ) | 自动固定,这是唯一的原因 这个化妆品赚到一个插槽 |
no-restricted-syntax (仅逻辑属性) | 新来 - 见下文 |
charcuterie/* (构成部分选择) | 选入,但拾取子集——见下文 |
charcuterie/* (弹性溢出) | 这里新建, 并选入 - 见下文 |
所有的东西 charcuterie/* 规矩在 一个名称空间下的插件对象页:1
内 src/plugin.js平面配置抛出 Cannot redefine plugin 当两个块注册时
一个带有两个不同对象的命名空间,下面两个块将启用
独立——所以两个工厂都给ESLint提供了同样的参考. 第二个命名空间
(单位:千美元)charcuterie-layout这是另一种选择,但被拒绝:你们必定要记住
每个规则在写禁用注释时所用的前缀,以及错误的前缀
eslint-disable 沉默无声.
仅逻辑属性
这舰队的每一个空间价值都是逻辑消耗的—— ps-编号 :pe-, (中文(简体) ), ms-编号 :me-, (中文(简体) ),
start-编号 :end-, (中文(简体) ), border-s编号 :border-e, (中文(简体) ), text-start编号 :text-end。它没有代价
今天,并让RTL 几乎在以后自由, 这就是为什么它是一个灵通规则而不是一个
偏好:偏好持续到第一人匆忙.
范围故意狭窄 —— 说 className 字符串字节和模板块
只有这个 有形财产 名称 样式对象( e)left, (中文(简体) ), paddingRight(没有)
匹配,因为 left 财务报告和审定财务报表 right 太多的合法身份识别器
地点 : getBoundingClientRect().left,浮动 UI 设置,渐变停止。 A 类
规则是哭狼被关闭, 而关闭规则执行
没什么 相同的推理使对比门 范围控制边界
而不是屏幕上的每行.
模式的锚是负载的,和 __fixtures__/logicalDirectionClassName.tsx
以证明: border-red-500 包含 border-r, (中文(简体) ), rounded-lg 包含
rounded-l, (中文(简体) ), place-items-center 开始于 pl这三个人必须保持干净.
构成部分选择——选入
2026-08-10测得的六起重案组 继续通过图书馆 原始内容。 文件一直没有到位,没有移动这些文件 数字,这是一个提示规则的整个参数: 医生读一次,规则是 执行每个保存.
| 规则 | 着火了 | 联系到 | 在舰队中测量 |
|---|---|---|---|
charcuterie/no-raw-anchor | <a> | TextLink,或 ButtonLink 当导航显示为按钮时 | 14个在mux-magic,10个在画廊倒装,31个在bambuddy |
charcuterie/no-raw-button | <button> | Button,或 IconButton 当控件仅为图标时 | 舰队中的每一个图标行 |
charcuterie/no-raw-select | <select> | Listbox (短,富)或 Combobox (长,可搜索) | 134个在巴姆巴迪,19分,积分市场2分 |
charcuterie/prefer-listbox-over-select | <Select> | Picker (滴入), Listbox,或 Combobox —— 说 Select 实值 已贬值,没有每个地点的例外 | 2026-08-20 折旧 |
charcuterie/no-clickable-non-interactive | onClick 打开 <div>编号 :<span>编号 :<li>没有 role 或者说 tabIndex | Button编号 :IconButton 行动, TextLink编号 :ButtonLink 导航 | points-market/…/AppShell.tsx:26-28 ——一个键盘无法到达的标题 |
charcuterie/no-navigation-in-click-handler | navigate(), (中文(简体) ), router.push(), (中文(简体) ), location.href = 在一个 onClick | TextLink编号 :ButtonLink 带有 href | 所有复杂的通道,和邮件过滤器的整个外壳 |
charcuterie/require-suppression-reason | 禁用上方任何规则但没有 -- reason | 说为什么在一行 | —— 说 |
prefer-listbox-over-select 逃生舱门是一条规则 没有 呼叫地点
判决: Select 因此,禁用者引用的是决定记录,而不是
因为它本身。 已经是本地人了 Select 是一个转换的积压——
Picker 一样 label 编号 : options 编号 : value 编号 : onChange。 。 。 .
两个链接组件构成 真实 <a href>中键,
ctrl-点击,打开-in-new-tab,复制链接和浏览器状态栏预览所有工作,以及
没有一个在点击处理器上工作。 链接去某个地方;按钮在这个页面上行事.
IconButton 获得自己的规则 出于同样的原因—— 仅一个图标 Button 已经
只能用一个格言来称呼它 IconButton 需要一个 label。 。 。 .
连接它
createComponentChoiceRules({
// The app's own source. `packages/ui/**` is deliberately not
// in here.
files: ["packages/web/**/*.tsx"],
})
或只是拾取子集—— no-raw-select, (中文(简体) ), prefer-listbox-over-select,则
镇压警卫——这就是什么 createAppConfig 默认打开,什么
4个重新登记在存在之前:
createPickerRules({ files: ["packages/web/**/*.tsx"] })
子集不是妥协;而是决定与决定之间的界线
积压. Select 在不保留每个呼叫地点例外的情况下进行折旧 Picker 实值
所以那对子里没有什么是判决 其他五个都叫A
扫 -- no-raw-button 舰队每一图标行都起火
{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}一个明确的 componentChoice: "all"。 。 。 .
与 Biome 而不是 ESLint 相同的两个错误
@charcuterie/biome-config/app,表达他们
作为 noRestrictedElements 财务报告和审定财务报表 noRestrictedImports。 。 。 .
这是故意的. 5个应用软件在被收养的那天会红掉 和一个配置 将整个回波变为红色是一个被重回而不是迁移的配置——所以是 它自己的块,当一个应用程序准备好修复它找到的东西时添加.
files 也是整个机制 保持规则 @charcuterie/ui,它使得
原始内容 <a>, (中文(简体) ), <button> 财务报告和审定财务报表 <select> 因为它的正确性 实值 图书馆.
点 files 在应用程序源和库中从未匹配;
__fixtures__/uiPackage/rawElements.tsx 说,因为范围界定机制 没有人
测试是一种无声地停止范围界定的范围界定机制.
这个街区 七个真正的插件规则没有 no-restricted-syntax 类似
逻辑属性一——插件对象在此包中内含,所以仍然有
没有额外的安装, 版本或发布 。 只有两种不同的规则身份才能做:
- 逃生舱关了它的名字. A 类
no-restricted-syntax压制是一无是处,所以沉默一个生<a>一行也会沉默 上面的逻辑属性选择器. - 平面配置取代了规则选项而不是将它们合并. 两块都设置好了
no-restricted-syntax与相重叠的花纹相重叠,只留下后来的选手 悄悄地跑着.
逃生舱
每个规则都有一条,它必须有一个理由:
// eslint-disable-next-line charcuterie/no-raw-select -- posted by the browser with no JS on the page at all
<select name="theme">…</select>
charcuterie/require-suppression-reason 报告禁用其中任何一条没有
-- reason 之后,包括一条被子 // eslint-disable-next-line (让他们沉默)
也一样。 其他人的"禁用"评论被单独放出. 没有原因 规则停止
没有人知道为什么 本地元素是正确的呼叫, 下一个代理复制
模式——这就是整个区块存在的修复失败.
Flex 溢出——选入
一天内四次独立重新发现一个CSS规则 (2026-08-11), 期间
整个舰队的起伏 @charcuterie/[email protected]。17px型斜坡 消耗了懒惰
在11个重pos中,有5个隐藏了潜在布局错误,4个相同
形状:包含一个长而不可破的符号的弹性行.
弹性项目的自动最小尺寸与它的内容相对应 分钟内容
宽度因此,一个没有突破机会的标志 成为了排的地板 并推出它的
从容器里出来的兄弟. min-width: 0 让 项目 收缩但什么都不做
页:1 文本,然后会溢出 overflow-wrap: anywhere 缩小分量
大小本身.
| 规则 | 着火了 | 默认 | 从哪里来 |
|---|---|---|---|
charcuterie/no-unconstrained-flex-text | a 文本元素 (span, (中文(简体) ), h1– 说吧 –h6, (中文(简体) ), p, (中文(简体) ), label,.) 渲染 {dynamic} 文本作为弹性的直列子 横行,在其中无法逃脱 className | warn | 画廊下载器 ErrorRow,点-市场 ShopPage (标题+最近购买的芯片) |
charcuterie/no-shrink-0-with-flex-wrap | shrink-0 财务报告和审定财务报表 flex-wrap 在同一元素上,在弹性行内 | error | 夹克 RipCard ——它发现同样的bug uncount in HeldBayCard 财务报告和审定财务报表 QuarantinedBayCard |
任何逃生都算数 因为四道运去的修补是四道不同的修补.
min-w-0 wrap-anywhere (装货机,点-市场方向), flex-wrap + 键
shrink-0 (点-市场的价格行), truncate + 键 title (邮箱主机),和
删除 shrink-0 (rip-deck) (英语). 要求其中一人的规则对
其他三个。 接受: min-w-*, (中文(简体) ), truncate, (中文(简体) ), text-ellipsis, (中文(简体) ), line-clamp-*, (中文(简体) ),
overflow-hidden, (中文(简体) ), wrap-anywhere, (中文(简体) ), break-all, (中文(简体) ), break-anywhere, (中文(简体) ), w-*, (中文(简体) ), max-w-*, (中文(简体) ),
basis-*, (中文(简体) ), size-*, (中文(简体) ), shrink-0, (中文(简体) ), flex-none, (中文(简体) ), absolute, (中文(简体) ), fixed。 。 。 .
故意两分
no-unconstrained-flex-text 是一个 高压。它可以看到一行的文本孩子说
没有关于它是如何收缩的, 它不知道是否 {status} 实值 "OK" 或一个
300个字符 URL. 测量到整个舰队的6/49档案
画廊下拉机,点市5/11分,邮筛8/12分,夹克6/36分和
333/341 in mux-magic —— 音量低,但每次都有判断,所以 警告时A级
规则是将回波红了 而不是判决呼吁 是规则被删除而不是
满意了 一旦应用被扫描, 便推广它 :
createFlexOverflowRules({
files: ["packages/web/**/*.tsx"],
severity: "error",
})
no-shrink-0-with-flex-wrap 不是一个柔和的 shrink-0 将项目指向
最大内容,所以 flex-wrap 除了它 永远不能参与,所以它 错误。 。 。 .
它故意不这样做
<div>不是一个文本元素. 这是通用的盒子 和最常见的孩子 弹性行宽;包括将规则变为噪音生成器.- 静态文本被跳过 .
Cancel从来没有300个字符。 虫子来了 数据,因此{…}儿童数——而不是{children}或者说{rows.map(…)},哪个 做别人的标记 而不是文字运行. tabular-nums被跳过. 这是舰队的标记 一个有界位数运行. 没有排除,规则警告 河雀的三个特征{percentText}连 之后 运送的固定.- 列容器超出范围,任何集装箱也一样
flex-col变体:形状是有条件的,有条件的形状不是自信的发现. - A 类
className无法静态读取 联合国clsx(…)调用, 一个变量) 被跳过 -- 越狱很可能就在里面 报告它无法读到的东西最快 如何关闭. shrink-0+ 键flex-wrap需要一个灵活排行的父程序 . 内部aflex-col, (中文(简体) ),shrink-0抵制缩小 块 轴并不会影响元素的自包. 肌肉魔法师FileExplorerModal标题栏正是如此,本来是 规则的第一个假阳性 在它看到的第一个真实文件.
它没有抓住的一起案件,它明确指出: 邮件过滤器 LinkCard 主持人。 内容
溢出元素是网格柱内的一个块,而不是弹性项——上方的弹性行
它已经受到了正确的限制——因此没有任何弹性规则能够看到它。 那个还是个
truncate 通过阅读学习.
逃出舱门,和其他地方一样, 它欠一个原因:
{/* eslint-disable-next-line charcuterie/no-unconstrained-flex-text -- a closed enum, never longer than "running" */}
<span className="text-xs uppercase">{status}</span>
测试
src/houseRules.test.ts 运行真实 ESLint 课结束 src/__fixtures__/, (中文(简体) ),
如下 mux-magic/packages/tools/src/eslintBooleanPrefixRule.test.ts. 煽动
相对于实际的引擎,而不是规则-泰斯特绳 抓住了故障
repo最有可能被击中——这个规则配置正确,永远不会 应用, (中文(简体) ),
因为a files glob 或 解析器选项错误 。 那个沉默的不做是
与CI中的"干净"无法区分.
src/__fixtures__/ 拥有自己的 tsconfig.json 因此,类型意识规则可以解决
它,并且被排除在软件包本身的型号检查之外—— 固定装置是插入的,
不是来源.
__fixtures__/appPackage/unconstrainedFlexText.tsx 联 合 国 constrained… 双胞胎是
舰队在2026-08-11 实际固定的四行 从运输犯抄取
(下载器) 81e2c2a,点-市场 e6438b7邮件筛选 8ed11f4{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}你应该去
ce66aab)——前后. 诱导虫不在它的固定装置中的一条规则是
一条规则,没有人可以显示 捕获任何东西.