@aurelienbbn/oxlint-plugin-core

中文文档
0.4.1发布于 2026/10/14MITTypeScriptES Module构建来源已验证近 30 天下载 2361

TypeScript 项目自定义克林特规则.

中文使用文档
文档目录9 节
  1. QQ 处理或解释发现的错误
  2. 测试证明行为,决定性
  3. 公共合同的名称
  4. 工作空间成员仅通过出口开会
  5. QQ 评论描述代码,而非其历史
  6. QQ 块、 声明运行和退出站相隔
  7. * 移徙
  8. 登记的合同库存
  9. 信用概念

@aureienbn/克林特-插座-核心

npm downloads CI license node

18克林特规则适用于任何TypeScript代码库:吞入错误,片状或空心测试,生产中的测试代码,匿名公共合同,工作空间成员互相泄露,口语收缩.

pnpm add -D @aurelienbbn/oxlint-plugin-core oxlint   # oxlint >=1.82.0 <2.0.0
{
  "jsPlugins": ["@aurelienbbn/oxlint-plugin-core"],
  "rules": {
    "core/no-discarded-caught-error": "error",
    "core/no-weak-test-assertions": ["error", { "assertionHelpers": ["assertValid"] }],
    "core/no-test-sleeps": "error"
  }
}

无预设,一种自定义. 启用每个规则的名称; 每一个修补, 但补丁规则的空白行需要一个工具无法做出的决定 .

按工作、选项和决定分列的规则
任务规则
错误no-discarded-caught-error, (中文(简体) ), no-error-message-matching
测试no-weak-test-assertions, (中文(简体) ), no-test-sleeps, (中文(简体) ), no-stubbed-subject, (中文(简体) ), no-ambient-nondeterminism-in-tests, (中文(简体) ), no-vitest-mocking, (中文(简体) ), no-vitest-in-source, (中文(简体) ), no-test-logic-in-production
合同no-exported-anonymous-object-return, (中文(简体) ), no-multi-positional-parameters, (中文(简体) ), no-mutable-exported-state, (中文(简体) ), no-reexport-only-modules, (中文(简体) ), no-let
工作空间workspace-boundaries
qq 评论no-dead-comments
qq 版式padding-line-between-statements (自动修复), padding-before-exit (自动固定,已贬值)
规则选项默认
no-discarded-caught-errorlogOnlyCallees["console", "logger", "log"]
reasonMarker"REASON"
includeTestFilesfalse (测试文件豁免)
no-ambient-nondeterminism-in-testsallowClockfalse· ; true 只放弃时钟读取
no-weak-test-assertionsassertionHelpers[]
no-vitest-mockingforbidSpiesfalse (单位:千美元)vi.fn, (中文(简体) ), vi.spyOn 允许)
no-reexport-only-modulesallow["index.ts"]
no-multi-positional-parametersexemptFunctionNames, (中文(简体) ), exemptFileBasenames[], (中文(简体) ), []
padding-line-between-statements爱斯林特的 { blankLine, prev, next } 列表下方显示的配置
workspace-boundariesapps (可部署的成员目录)["apps/**"]
forbidden (单位:千美元){ from, to, message? } 成员目录对数)[]
规则决定它的固定需要
no-ambient-nondeterminism-in-tests场景的地盘,冰冻瞬间,或种子
no-dead-comments赤裸的 TODO: 跟踪或删除
no-discarded-caught-error重投、 错误上的分行或记录一个原因
no-error-message-matching稳定的区分符( 类、 标记、 代码) 取决于错误源
no-exported-anonymous-object-return计划/ 类型名称及其导出地点
no-let调整数值构造, 不交换关键字
no-multi-positional-parameters更新呼叫者, 选择属性名称
no-mutable-exported-state工厂、不可改变的合同或附属边界
no-reexport-only-modules将每个消费者都改道到自有模块
no-stubbed-subject由合作者替换
no-test-logic-in-production注入依赖性或公共合同
no-test-sleeps投票条件或假计时表
no-vitest-in-source合适的测试工具或依赖性接合
no-vitest-mocking依赖边界和假边界
no-weak-test-assertions每项试验所称的可观测结果
workspace-boundaries哪个成员拥有共享代码,哪个子路径输出

QQ 处理或解释发现的错误

try {
  await sync();
} catch (error) {
  console.error(error);
  return [];
} // ❌ only logged
promise.catch(() => null); // ❌ discarded
promise.catch(handleFailure); // ✅ handed to a function
try {
  access(path);
  return true;
} catch {
  // REASON: a missing file is the negative answer here
  return false; // ✅ reasoned
}

return err.message === "Not found"; // ❌ no-error-message-matching
return error instanceof TimeoutError; // ✅ class, tag, or code
no-discarded-caught-error 财务报告和审定财务报表 no-error-message-matching 细节

no-discarded-caught-error 覆盖 catch 条款,内在 .catch(fn)的第二个参数 .then,当处理器从未丢出(或返回) Promise.reject(...)装订不存在或只达到 console.* 编号 : logger.* 编号 : log.*。 。 。 .

try {
  return await load(id);
} catch (error) {
  throw new LoadError({ id, cause: error });
} // ✅ rethrown
try {
  return await load();
} catch (error) {
  if (isNotFound(error)) return undefined;
  throw error;
} // ✅ inspected
大小写行为
计为处理将错误传递到任何非日志函数, 返回, 测试
逃生舱门REASON: <≥ 3 words> 处理器内部; 承诺回调也直接位于语句上方. 占位符( E)todo, (中文(简体) ), ignored, (中文(简体) ), this is fine不要数
catch {} (字面为空)左转 eslint/no-empty
Effect.catch(...)qq 资本化接收器是命名空间, 跳过
有实例的建构者 .catch(fn)qq 需要一个理由或禁用注释

no-error-message-matching数字 : .message 与字符串字词相对应,以 includes 编号 : startsWith 编号 : endsWith 编号 : match 编号 : search 编号 : indexOf 编号 : RegExp#test (亦于 toLowerCase()- 式调用),或用作 switch 歧视者. String(error), (中文(简体) ), error.toString(), (中文(简体) ), `${error}` 也算在装订量上 测试文件豁免 。 错误形状 = 抓取绑定,拒绝处理器参数,或名称类似 e, (中文(简体) ), err, (中文(简体) ), error, (中文(简体) ), cause, (中文(简体) ), reason, (中文(简体) ), ex, (中文(简体) ), exception, (中文(简体) ), *Error, (中文(简体) ), *Err, (中文(简体) ), *Exception。 。 。 .

测试证明行为,决定性

no-weak-test-assertions 法官每个测试:一个强有力的断言可以澄清这一点.

弱 强强
expect(load()).toBeDefined()expect(load()).toEqual({ id: 1 })
expect(() => run()).not.toThrow()expect(() => run()).toThrow("boom")
toHaveBeenCalledWith(expect.any(Object))toHaveBeenCalledWith(expect.any(String), 30)
await new Promise((r) => setTimeout(r, 500)); // ❌ no-test-sleeps
await vi.waitFor(() => expect(state()).toBe("done")); // ✅ poll the condition

// cart.test.ts
vi.spyOn(cart, "total").mockReturnValue(30); // ❌ no-stubbed-subject
vi.spyOn(pricing, "rate").mockReturnValue(2); // ✅ another module

export const send = (mail) => (process.env.NODE_ENV === "test" ? undefined : deliver(mail)); // ❌ no-test-logic-in-production

使用 Vitest 内源测试回合的项目 no-test-logic-in-production 离开那些文件.

每个弱的形状,睡眠的形状,非决定性的来源, 和只测试标记

二. 弱项

弱 强强
.toBeTruthy()准确的数值
expect(true).toBe(true) (文对文expect(3).toBe(add(1, 2)) (文学与计算)
expect(result).toEqual(result) (值对自身)expect(render()).toMatchSnapshot()
expect(fn()).toBe(5) 之后 fn.mockReturnValue(5)fc.assert(fc.property(…))
toEqual(expect.anything())toEqual({ id: expect.any(String), total: 30 })
赤线 toHaveBeenCalled() 编号 : toBeCalled().not.toHaveBeenCalled() ("从来没有叫"是准确的)
toBeInstanceOf(Object)toEqual(expect.any(Order))
expect(typeof x).toBe("string")

通关卡= expect.anything(), (中文(简体) ), expect.any(Object | Function | Array | String | Number | Boolean), (中文(简体) ), expect.objectContaining({}), (中文(简体) ), expect.arrayContaining([])。 。 。 . 裸体 toThrow() 不计算 : vitest/require-to-throw-message 拥有它, 被绑在测试文件上 @aurelienbbn/oxlint-config。 。 。 .

睡觉 (单位:千美元)*.test.* 编号 : *.spec.*(中文(简体) )

await new Promise((r) => setTimeout(r, 500)); // ❌ timer-only executor
import { setTimeout as sleep } from "node:timers/promises"; // ❌ also `scheduler`
await sleep(250); // ❌ sleep / delay / wait / pause + numeric literal

调用文件 vi.useFakeTimers() 跳过第一个和最后一个形状: 它们的计时器不是壁点.

环境非决定因素 (概念:"在"试验"中消除非决定性","马丁·福勒"

* 开火除非我们..
toLocaleString 编号 : toLocaleDateString 编号 : toLocaleTimeString 没有地方—— 说
localeCompare 没有第二个参数—— 说
Intl.*Format 构造时没有区位—— 说
Math.random()—— 说
new Date() (没有参数), Date.now(), (中文(简体) ), performance.now()文件调用 vi.useFakeTimers 编号 : vi.setSystemTime,或 allowClock: true

阴影 Date 编号 : Math 编号 : Intl 保持安静.

僵尸主题 (单位:千美元)<stem>.test.* 编号 : <stem>.spec.*(中文(简体) )

import * as cart from "./cart"; // also ./cart.js, ../cart/index
vi.spyOn(cart, "total"); // ✅ pass-through spy

命名空间或默认导入同位素相对模块;罐装行为通过 mockReturnValue, (中文(简体) ), mockResolvedValue, (中文(简体) ), mockRejectedValue, (中文(简体) ), mockImplementation (和 …Once),被锁链或被绑在间谍身上 const. 包的进口保持沉默.

生产中的测试逻辑 (概念:生产中的试验逻辑),G. Meszaros, x 单位测试模式) (中文(简体) ). 在测试文件中沉默, Vitest 配置和设置文件, /test-utils/, (中文(简体) ), /testing/ (路径) no-vitest-in-source 允许).

export const send = (mail) => (process.env.NODE_ENV === "test" ? undefined : deliver(mail)); // ❌ also import.meta.env.MODE === "test"
export const retries = process.env.VITEST ? 0 : 3; // ❌ also VITEST_WORKER_ID, JEST_WORKER_ID, import.meta.vitest
export const __testing__ = { parse }; // ❌ name marks it test-only
/** Visible only for unit tests. */
export const parse = (input) => input; // ❌ comment marks it test-only

只测试名称 : __test__ 编号 : __tests__ 编号 : __testing__, (中文(简体) ), _internal(s), (中文(简体) ), internals, (中文(简体) ), testOnly*, (中文(简体) ), *ForTest 编号 : *ForTests 编号 : *ForTesting. 直接在出口上方的“出口用于测试”或“仅用于测试”等评论.

虚拟 API : no-vitest-in-source 导入报表 vitest 非试验来源; no-vitest-mocking 通过 Vitest 模拟API 替换报告模块 vi.fn 编号 : vi.spyOn 与 forbidSpies: true。 。 。 .

公共合同的名称

export const getUser = () => ({ id: user.id }); // ❌ no-exported-anonymous-object-return
export const load: Load = () => ({ id: user.id }); // ✅ named contract

const loadUser = (userId: UserId, includePosts: boolean) => userId; // ❌ no-multi-positional-parameters
export function loadUser(input: { userId: UserId; includePosts: boolean }) {} // ✅ object input

export let currentUserId = undefined; // ❌ no-mutable-exported-state
export * from "./user.js"; // (whole file)                    // ❌ no-reexport-only-modules, unless listed in `allow`
let total = 0; // ❌ no-let (also `var`)

工作空间成员仅通过出口开会

workspace-boundaries 读取最近的成员 pnpm-workspace.yaml (或 package.json workspaces评议 import, (中文(简体) ), export … from,以及 import() 两名委员之间:

// packages/kernel/src/tick.ts
import { boot } from "@game/client"; // ❌ packages never import apps (members under `apps`)
import { arrow } from "../../ranger/src/arrow.ts"; // ❌ relative path into another member
import { body } from "@game/physics/src/body.ts"; // ❌ not opened by @game/physics `exports`
import { body } from "@game/physics"; // ✅ an exported subpath
{
  "core/workspace-boundaries": [
    "error",
    {
      "apps": ["apps/**", "infra/**"],
      "forbidden": [
        { "from": "packages/features/*", "to": "packages/features/*", "message": "features meet through the kernel" },
        { "from": "packages/kernel", "to": "packages/adapters/**" }
      ]
    }
  ]
}

一个没有子路的表象 exports 只打开其根;一个子路径映射到 null 关闭; * 匹配至少一个字符,如节点。 仅按类型进口和再出口被判断为价值进口:一种类型的依赖仍然是依赖. require() 财务报告和审定财务报表 import x = require() 未读。 每个成员以外的文件(root configs,脚本)从不被判断. 目录模式 : * 在一个部分之内, ** 横跨各段。 成员每个 lint 进程读取一次: 添加一个后重新启动编辑器的语言服务器 。 循环与 import/no-cycleKnip的死档案.

QQ 评论描述代码,而非其历史

// Updated to use the new pricing API    ❌ change narration (line comments)
// TODO: rename this contract            ❌ untracked
// TODO(#123): rename this contract      ✅ tracked (also CART-42 or a link)
/** Updated when the cache expires */    ✅ docblock describing behavior
每个 no-dead-comments 形状
火灾保持安静
// NEW: 编号 : // Updated: 编号 : // Fixed: 标签// New customers receive a welcome discount
// Previously this… 编号 : // Formerly: 编号 : // Was:// Previously seen cursors are skipped
// Added … as requested 编号 : … for the review// TODO CART-42: tighten the types 或链接
仅行注释 : // Updated to… 编号 : // Changed from… 编号 : // Now returns… 编号 : // No longer supports…

还有关闭- brace 标签和占位符脚手架; 叙述只在注释开始时才算数 .

QQ 块、 声明运行和退出站相隔

padding-line-between-statements 选择 ESLint 的选项( 列表 { blankLine, prev, next })及其语句类型. 一个声明上方的评论块属于它: 空白行位于注释上方,对行的评论则以该说明结尾. 自动插入或去除空白行.

function start(runtime) {
  const config = load();
  // Planning runs need no config.
  if (runtime.planning) {
    return;
  }
  runtime.apply(config); // ❌ twice: above the comment, and below the block
}

function start(runtime) {
  const config = load();

  // Planning runs need no config.
  if (runtime.planning) {
    return;
  }

  runtime.apply(config); // ✅
}

在没有选项的情况下启用,它应用了此配置; 任何选项全部替换:

{
  "core/padding-line-between-statements": [
    "error",
    { "blankLine": "always", "prev": "*", "next": ["block-like", "multiline-expression"] },
    { "blankLine": "always", "prev": ["block-like", "multiline-expression"], "next": "*" },
    { "blankLine": "always", "prev": ["const", "let"], "next": "*" },
    { "blankLine": "any", "prev": ["const", "let"], "next": ["const", "let"] },
    { "blankLine": "always", "prev": "*", "next": ["return", "throw"] }
  ]
}
重叠状态
padding-before-exitqq已贬值:默认配置覆盖它,以及 { "blankLine": "always", "prev": "*", "next": ["return", "throw"] } 独自复制它。 打开它 padding-line-between-statements。 。 。 .
effect/padding-after-dependencies补充:它也分开 yield* a类服务依赖关系 const 在它们下面的逻辑, 声明是免费的。 两条规则都插入了同一条单行空白.

* 移徙

现在报告示例修补( 无自定义)
别名导出const read = () => ({ id: 1 }); export { read as load };名称返回类型,与直接导出相同
化名维特spec.each(rows)("exists", row => check(row).toBeDefined()) 何时 spec 编号 : check 别名 test 编号 : expect确定可观测值,或登记真正的断言助手
合同:每个规则都做什么,没有解决
专题合同
建筑政策已命名的导出对象合同和对象样式参数是策略,而不是错误的证明
公众回报分析qq 直接导出, 同文件的导出列表/别名, const 转发别名, 默认识别符, 明显超载签名 qq 嵌入式帮助器返回保持私密 qq 外部再出口和包入点 转录未解决
调用次数外部强制的回调异常
no-let可选:故意局部变异可以更清晰更快
可变出口处理的远期出口没有合并当地申报
微弱的断言每个测试; 识别真实 expect 链条和有意义的快照/财产断言;任意的匹配者命名方法不是证据
测试识别已命名的别名、 命名空间导入、 未阴影的全局, each,有条件的修改符。 本地的影子在维特规则之外 快检查所导入的别名的断言支持
assertionHelpers信任这个本地名称的电话: 帮助者的行为合同是针对你的. 跨模块包接推论不支持

登记的合同库存

从软件包导出 pnpm catalog. 规则特定选项和限制在上文和源测试中都有描述.

规则/检查触发或审查范围
no-ambient-nondeterminism-in-tests根据主机区、 真实时钟或 Math.random(): 本地化敏感格式化,没有本地化, new Date() 编号 : Date.now() 编号 : performance.now() 没有假计时器, 和无种子随机.
no-dead-comments否定已死评论:关闭-brace标签,占位符脚手架,没有被跟踪的TODO,并评论会描述一个更改而不是代码.
no-discarded-caught-error需要抓取条款或内置承诺拒绝处理器, 绝不重写和忽略或只记录错误, 以便携带一个合理的标记注释 .
no-error-message-matching通过字符串比较,子字符串或 regex 匹配,或切换,在错误消息的文本上取消分支.
no-exported-anonymous-object-return对直接输出的函数,通过本地化名,或作为默认标识符,要求命名对象返回合同.
no-let拒绝让和 var 声明支持 康斯特和面向表达的代码.
no-multi-positional-parameters需要对象输入而不是函数的多位置参数.
no-mutable-exported-state拒绝导出可变模块状态 .
no-reexport-only-modules除允许的入点桶外,仅再导出其他模块的断开模块.
no-stubbed-subject丢弃 a <stem>.test 文件来自替换其自身的行为 <stem> 模块通过 vi.spyOn(...) 然后是罐头回放值或执行.
no-test-logic-in-production使来自测试跑道或测试模式上的分支和来自被命名或评论为仅用于测试的现有出口成员的生产文件失效.
no-test-sleeps拒绝在测试文件中使用墙钟睡眠:设置Timeout支持的承诺,节点:timers/promise sleep,并等待时间固定的睡眠助手.
no-vitest-in-source拒绝从非测试源文件导入 vitest .
no-vitest-mockingDisallow Vitest 嘲笑API 支持确定性测试双人.
no-weak-test-assertions否定个人测试 其唯一的断言 检查存在, 赤裸裸的呼叫, a typeof 或者说 Object 实例中,仅使用通配符的参数,对自身或字面的值,或本地模拟本身的返回值.
padding-before-exit需要返回前的空白行或扔出跟随同一块中另一个语句的语句,切换大小写或程序正文.
padding-line-between-statements需要或不允许语句之间的空行,其中ESLint的选项和一个注释块作为下方语句的一部分被计算;默认为被下方语句块,多行表达式,声明运行和退出.
workspace-boundaries将工作空间成员分开:软件包从不导入应用软件,成员只能通过软件包. json导出和从不通过相对路径互相输入,配置的成员对从不导入.

信用概念

版本与发布记录

@aurelienbbn/oxlint-plugin-core 共发布 4 个版本,首次发布于 2026/09/25;过去 12 个月发布了 4 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.4.1latest2026/10/1424 天前
0.3.02026/10/011 个月前
0.2.02026/09/251 个月前
0.1.02026/09/251 个月前

关于 @aurelienbbn/oxlint-plugin-core 的常见问题

@aurelienbbn/oxlint-plugin-core 是什么?

TypeScript 项目自定义克林特规则.

如何安装 @aurelienbbn/oxlint-plugin-core?

在项目目录运行 npm install @aurelienbbn/oxlint-plugin-core,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:oxlint(>=1.82.0 <2.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@aurelienbbn/oxlint-plugin-core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aurelienbbn/oxlint-plugin-core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aurelienbbn/oxlint-plugin-core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js ^22.19.0 || ^24.11.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@aurelienbbn/oxlint-plugin-core 可以商用吗?

@aurelienbbn/oxlint-plugin-core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@aurelienbbn/oxlint-plugin-core 的下载量有多少?

根据 npm 官方下载统计,@aurelienbbn/oxlint-plugin-core 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,361 次,日均约 79 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aurelienbbn/oxlint-plugin-core 还在维护吗?

最新版本 0.4.1 发布于 2026/10/14(24 天前);至今共发布 4 个版本,其中过去 12 个月发布了 4 个;首次发布于 2026/09/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。