@ amritk/ lint
一个快速,格式不可知的JSON/YAML风格-指针linter——JSON Schema验证和自定义规则,有精确 line:column 发现.
概览
@amritk/lint 线条 任意 JSON 或 YAML 文档对照您定义的规则设置 。 规则将节点匹配为 乔森帕斯 (单位:千美元)given运行 a 函数 (单位:千美元)then在每场比赛中——结构验证与 杰森·斯克马,样式检查(casing, (中文(简体) ), pattern, (中文(简体) ), alphabetical, (中文(简体) ), length,...),或你自己的自定义功能. 每个发现都包含一个精确 line:column 范围,因为解析器保留每个节点的源位置.
没错 格式不可识别:核心发动机船没有内置规则,对OpenAPI或任何其他计划一无所知——你带来规则. 这是以JSON Schema和自定义规则为核心的 JSON/YAML 风格-向导林入.
对 OpenAPI 和 AsyncAPI 来说 @amritk/lint/rules/openapi 财务报告和审定财务报表 @amritk/lint/rules/asyncapi 发动机上面的备用预置装置——见 OpenAPI 规则集 财务报告和审定财务报表 AsyncAPI 规则集 见下文.
CLI住在美国 mjst 二进制为 mjst lint;这个软件包是它背后的程序库.
安装
npm install @amritk/lint
# or
pnpm add @amritk/lint
# or
bun add @amritk/lint
对于命令行,安装 @amritk/mjst 运行 mjst lint (见 国 际 页:1.
使用量
刻入文档
import { lintDocument } from '@amritk/lint'
const ruleset = {
rules: {
'require-name': { given: '$', severity: 'error', then: { field: 'name', function: 'truthy' } },
'name-kebab': { given: '$.name', severity: 'warn', then: { function: 'casing', functionOptions: { type: 'kebab' } } },
},
}
const findings = await lintDocument('version: 1\n', { ruleset, source: 'service.yaml' })
// → [{ code: 'require-name', message: 'The value must be truthy', path: ['name'],
// severity: 0, source: 'service.yaml', range: { start: { line: 0, character: 0 }, … } }]
severity 是一个 DiagnosticSeverity —— 说 0 错误, 1 警告者, 2 资料, 3 提示——和 range 是一个基于零的 { line, character } 横跨时,您可以使出一股出息.
已解析的 Lint 数据
已解析文档的调用器——读取并规范其的加载器——通过数据而不是文本,解析被跳出:
const findings = await lintDocument({ data: spec, text: source }, { ruleset, source: 'openapi.json' })
text 是可选的,只读到要放 line:column jSON 文本被扫描, 仅用于包含结果的路径, YAML 文本被解析到第一次查找。 呼唤者正常化的路径在文字中给其最亲近的祖先添加了决心,没有 text 每个范围都是零范围。 由于数据没有解析,因此没有解析诊断: parserOptions (单位:千美元)duplicateKeys, (中文(简体) ), incompatibleValues),而语法错误只在文本被擦去时被报告. 重写文档( 自动修正) 需要的插件 text。 。 。 . createDocumentFromData(data, { text }) 为低层建立同一文档 createLinter(ruleset).run()。 。 。 .
JSON Schema 验证
内置 schema 函数在匹配的节点上运行任意的 JSON Schema( 2020- 12 草案),通过 @amritk/runtime-validators数字 :
const ruleset = {
rules: {
'config-schema': {
given: '$',
severity: 'error',
then: { function: 'schema', functionOptions: { schema: { type: 'object', required: ['port'], properties: { port: { type: 'integer' } } } } },
},
},
}
await lintDocument('port: not-a-number\n', { ruleset })
// → a `config-schema` finding pointing at `port`
自定义函数和 extends
一个规则集可以从另一个文件中拉出规则并按名称加载自定义函数(相对于声明它们的规则集解出):
# .lint.yaml
extends:
- ./base.yaml # a file path or an npm package
functions: [no-secrets] # loaded from ./functions/no-secrets.{js,cjs,mjs}
rules:
no-secrets:
given: $..*
severity: error
then: { function: no-secrets }
import { lintDocument } from '@amritk/lint'
await lintDocument(source, { ruleset: definition, rulesetBasePath: '/path/to/config/dir', source: 'doc.yaml' })
自定义函数有签名 (value, options, context) => { message: string, path?: JsonPath }[]。 。 。 .
信任边界——谁可以写规则集
A 类 文档 被内嵌是不信任的输入:它被解析,行走,并匹配,其中没有任何内容可以执行代码. A 类 规则 不是输入——它是配置,它和运行线条的过程一样特权:
extends接受 任意 路径.rulesetBasePath在解决之处 开始而不是栅栏:../../../elsewhere/rules.yaml财务报告和审定财务报表/etc/rules.yaml决心和负载.extends页:1.js编号 :.cjs编号 :.mjs文件,以及名为functions,为required - 说明 这是代码执行,根据设计。定制功能就是这样运作的.- YAML/JSON规则是 数据: 他们
given过滤表达式( e)[?(...)])被解析并被解释,从未被评价为JavaScript,因此只有数据的规则集不能运行代码. 一个规则集 命名a.js文档仍然可以.
所以:从您信任的源头装入规则来运行脚本。 如果你接受来自不太信任的地方的规则(多租户服务、叉出公关),通过 restrictTo ——每个 extends 目标和自定义函数必须在以下情况下解决:
await lintDocument(source, {
ruleset: definition,
rulesetBasePath: '/srv/rulesets/tenant-42',
restrictTo: '/srv/rulesets/tenant-42', // refuse anything resolving outside this tree
})
restrictTo 默认关闭并缩小 哪个 文件规则可以命名。 这不是沙盒: .js 文件在允许的根内运行时仍然有全部权限。 OpenAPI预设使用相同的选项—— createOpenApiRuleset(definition, basePath, { restrictTo })。 。 。 .
还有一件事 restrictTo 不涵盖:规则集写作的正则表达式 — in pattern是因为 match编号 :notMatch,或作为文字 [?(...)] 过滤器 – 由文档编辑并对照文本运行 。 一种模棱两可的图案(与重叠的字符类相比被取消的修饰符)可以灾难性地回溯到一个为触发它而设计的输入上,所以一个敌对的 文档 可以挂接器通过正弦 规则 提供。 内在规则故意避免这种模式(见注4) casing),在所写的规则中保持同样的纪律,并且更喜欢固定的图案和毫不含糊的替代品.
自动修复
fixDocument 运行线条并应用 a FixerRegistry ——按规则按键的固定器 code 将结果映射到格式化的保存文本编辑器上,然后将结果报告给固定点:
import { fixDocument, type FixerRegistry } from '@amritk/lint'
const fixers: FixerRegistry = {
'no-trailing-slash': {
fix: ({ diagnostic, data }) => {
const value = (data as Record<string, unknown>)[diagnostic.path[0] as string]
return typeof value === 'string' ? { op: 'setValue', path: diagnostic.path, value: value.replace(/\/$/, '') } : undefined
},
},
}
const { output, applied, remaining } = await fixDocument('host: api.example.com/\n', { ruleset, fixers })
// output === 'host: api.example.com\n'
引擎船没有内置固定器(规则代码由你来定义),所以默认注册为空并 fixDocument 在你提供之前是禁止的.
options.ruleset 使用定义( 以内置函数构建在此) 或 a Ruleset 你创造了你自己。 当规则需要函数或格式检测器时,通过一个已建规则, 包根没有这个功能或格式检测器—— 例如, OpenAPI 预设, 否则所有规则都会作为未知函数/ 未匹配格式被跳过 :
import { fixDocument } from '@amritk/lint'
import { createOpenApiRuleset, oasFixers } from '@amritk/lint/rules/openapi'
const { output, applied } = await fixDocument(source, {
ruleset: createOpenApiRuleset(),
fixers: oasFixers,
})
固定到一个固定点,10个通道封顶. remaining 来源于找不到可用来修补的通路,所以会汇合的通路不会第二次将已固定的文档擦入;只有会打出盖子的通路. 结果报告了结果: converged 实值 false 在文档仍在更改(通常是两个固定器相互抵消)时被击中后 passes 计算改变事物的通行证. applied 被规则代码和路径所去复制,因此报告可以安全地说出"固定的N问题".
提出调查结果
lintDocument 返回结构化 IDiagnostic[] - 每一个都有 code, (中文(简体) ), message, (中文(简体) ), path, (中文(简体) ), severity, (中文(简体) ), source,并基于零 range。 。 。 . source, (中文(简体) ), path 财务报告和审定财务报表 range 都在同一文件中描述同一个节点:作者所写的节点. 一条与内嵌的树相匹配的规则 $ref 目标,其调查结果在目标来源的申报中报告,而不是在目标来源的申报中报告 $ref 网站。 两个 $ref因此得出一个结论,而不是两个结论. 制作是打电话者的工作:打印出它们,序列化到JSON,或者将它们映射到编辑器或CI所消耗的任何东西上. 喷发器故意不输出“物”层(这与 prettier编号 :biome format(原始内容存档于2017-03-25)., which reformat source.
const findings = await lintDocument(source, { ruleset, source: 'doc.yaml' })
for (const f of findings) {
const { line, character } = f.range.start
console.log(`${f.source}:${line + 1}:${character + 1} ${f.code} ${f.message}`)
}
国 际
这个 mjst 二进制显示为子命令的线条,该子命令会打印一个压缩 file:line:col 报告:
mjst lint "**/*.{yaml,json}" -r .lint.yaml
没有 -r它发现一个 .lint.{yaml,yml,json,js,mjs} 规则从每个文件上走出来。 退出代码来源于 --fail-severity (默认) error) (中文(简体) ). 见 CLI 读取 为全旗参考.
规则格式
规则是普通对象(作者为YAML,JSON,或JS模块):
| 外地 | 说明 |
|---|---|
rules | 地图 name → rule规则 given (一个或多个 JSONPath 表达式), then (要运行的函数,或列表), severity (单位:千美元)error编号 :warn编号 :info编号 :hint编号 :off)和可选 message, (中文(简体) ), description, (中文(简体) ), formats, (中文(简体) ), recommended, (中文(简体) ), resolved (默认) true· ; false 将规则运行到文件的正文前 $ref (d) 减少参考资料, descent (默认) all· ; schema 保留 .. 调出 example, (中文(简体) ), examples, (中文(简体) ), default, (中文(简体) ), const 财务报告和审定财务报表 enum 因此,关于计划的规则与实例有效载荷不符) documentationUrl。 。 。 . |
then | { function, field?, functionOptions? } —— 说 field 缩小匹配到一个孩子( e)@key 目标属性名称). |
extends | 要继承规则的规则集(或列表)来自:文件路径或npm包. [target, 'recommended' | 'all' | 'off'] 控制它的贡献. |
functions 编号 : functionsDir | 自定义按名称加载的函数( 默认 dir) functions/) (中文(简体) ). |
overrides | Per -file -glob 规则的tweaks. |
aliases | 可重复使用 given 片段引用为 #alias。 。 。 . |
parserOptions | YAML 解析器严格度, 每个设置到一个严重性 . duplicateKeys 重复键(默认) error· ; off 允许它,最后的值获胜. incompatibleValues 默认关闭和报表 .nan 编号 : .inf 编号 : -.inf,哪个 JSON.stringify 悄悄地变成 null。解析器结果带有代码 parser。 。 。 . |
内建功能 : alphabetical, (中文(简体) ), casing, (中文(简体) ), defined, (中文(简体) ), enumeration, (中文(简体) ), falsy, (中文(简体) ), length, (中文(简体) ), or, (中文(简体) ), pattern, (中文(简体) ), schema, (中文(简体) ), truthy, (中文(简体) ), undefined, (中文(简体) ), unreferencedReusableObject, (中文(简体) ), xor, (中文(简体) ), typedEnum。 。 。 .
given - JSONPath子集
$,儿童和 ['child'] 访问, .. 递归血统, [*] 编号 : .* 通关卡, [a,b] 工会, [n] 指数(期末负数), [start:end:step] 切片块, [(@.length-1)], (中文(简体) ), ^ (家长)和 ~ (财产名称),和 [?(...)] 过滤器.
过滤器是 解析并解释, 未评价为 JavaScript ——规则是数据,不能执行代码. 支持的语法 :
| A. 背景情况 | @, (中文(简体) ), @.name, (中文(简体) ), @['name'], (中文(简体) ), @[0], (中文(简体) ), @property, (中文(简体) ), @parentProperty, (中文(简体) ), @parent, (中文(简体) ), @path, (中文(简体) ), @root, (中文(简体) ), $ |
| 运算符 | === !== == != < <= > >= && || ! - (负数),括号 |
| 文学 | 字符串,数字, true, (中文(简体) ), false, (中文(简体) ), null, (中文(简体) ), undefined, (中文(简体) ), void 0, (中文(简体) ), /regex/flags |
| 成员呼叫( C) | .length, (中文(简体) ), .indexOf(…), (中文(简体) ), .lastIndexOf(…), (中文(简体) ), .includes(…), (中文(简体) ), .startsWith(…), (中文(简体) ), .endsWith(…), (中文(简体) ), .match(/re/), (中文(简体) ), /re/.test(…), (中文(简体) ), .toLowerCase(), (中文(简体) ), .toUpperCase(), (中文(简体) ), .trim() |
语法之外的任何事物都是规则错误(createRuleset 扔出规则并命名规则)而不是一个悄悄匹配无一物的过滤器. 成员读取 仅拥有属性这样 @.constructor 财务报告和审定财务报表 @['__proto__'] 简单 undefined。 。 。 .
API 密码
| 导出 | 它会做什么 |
|---|---|
lintDocument(input, options?) | 解析 input 并涂入它反对 options.ruleset 定义,或 Ruleset 已建; 返回 IDiagnostic[]。 。 。 . input 是文本,或 { data, text? } 对于已经解析过的文档. |
lintDocumentWithResult(input, options?) | 喜欢 lintDocument返回时 { diagnostics, output?, pluginData } (包括任何插件的重写) output) (中文(简体) ). |
fixDocument(input, options?) | 启动并应用 options.fixers 转到固定点;返回 { output, fixed, applied, remaining, converged, passes }。 。 。 . |
createRuleset(definition?, basePath?, options?) | 将规则集定义规范化为可运行 Ruleset,对内置函数进行分层并解析 extends标注 (definition object, basePath, restrictTo) - 将你通过的定义视为冷冻. |
resolveNamedRuleset(name, basePath?, options?) | 决定 extends 引用(文件路径或npm软件包)它的定义. |
builtinFunctions | 内置规则功能的注册. |
发动机内部( :createDocument, (中文(简体) ), lint, (中文(简体) ), lintWithResult, (中文(简体) ), query, (中文(简体) ), validateRuleset, (中文(简体) ), parseWithPointers, (中文(简体) ), createFixPlugin,从包根再出,供高级使用; DiagnosticSeverity 和规则/诊断类型IDiagnostic, (中文(简体) ), RulesetDefinition, (中文(简体) ), IRuleDefinition,(...) 生活着 @amritk/lint/types 子路径.
OpenAPI 规则集
核心包是格式-不可知的,但OpenAPI已经足够常见,可以运送一个现成的预设. 它住在这里 @amritk/lint/rules/openapi 子路径 — 引擎上方的自存层 无依赖关系 超越什么 @amritk/lint 已经使用.
import { lint } from '@amritk/lint'
import { createOpenApiRuleset } from '@amritk/lint/rules/openapi'
// Defaults to `extends: [oas]` (recommended rules only, like `spectral:oas`).
const ruleset = createOpenApiRuleset()
const findings = await lint(spec, { ruleset })
createOpenApiRuleset(definition?, basePath?, options?) 构建可运行的 Ruleset 与 OpenAPI 函数和格式检测器在内置上分层,并与 extends 决议 oas 编号 : loupe:oas 编号 : spectral:oas 名称(最后两个接受这样现有的Spectral-tyle rulesets 延伸不变)。 启用每条规则 createOpenApiRuleset({ extends: [['oas', 'all']] }),或者通过你自己的定义来推翻断裂,添加规则,或者点 extends 在一个文件/ npm 包中. options.restrictTo 应用相同的 信任边界 软件包根 restrictTo 已经.
| 导出 | 它会做什么 |
|---|---|
createOpenApiRuleset(definition?, basePath?, options?) | 构建可运行的 OpenAPI Ruleset (职能+格式+) extends 页:1. |
resolveOpenApiRuleset(name, basePath?, options?) | 决定 extends 参考文献,包括 oas 编号 : loupe:oas 编号 : spectral:oas 名称. |
oas | 内置的OpenAPI规则集定义. |
oasFunctions 编号 : allFunctions | OpenAPI 特定功能; allFunctions =内置+OpenAPI. |
oasFormats | OpenAPI 版本检测器(oas2, (中文(简体) ), oas3, (中文(简体) ), oas3.0, (中文(简体) ), oas3.1, (中文(简体) ), oas3.2) (中文(简体) ). |
oasFixers | 用于机械可修复的 OpenAPI 规则的自动修复器(通过到 fixDocument 与已建的 OpenAPI 规则集并列). |
loadOasSchema(version) | 拉齐里加载了 OpenAPI 版本的官方结构元-schema('2.0' 编号 : '3.0' 编号 : '3.1' 编号 : '3.2'以原价出售 .json 从 spec.openapis.org (3.0/3.1.3.2逐字记录;2.0及其外部草稿04 metaschema refers inlined) 见 schemas/README.md。 。 。 . |
结构规则与 正式 spec.openapis.org 以原价出售的元计价器 .json (单位:千美元)schemas/. 3.0/3.1/3.2是字节拼写;只有2.0差异(其外部草稿-04 metaschema refs被内置,因为离线口译从不获取远程 refs,且其顶级) id编号 :$schema 键被丢弃). OpenAPI 3.1.3.2 通过局部将Schema Objects 表示为 JSON Schema 2020-12 $dynamicRef编号 :$dynamicAnchor,哪个 @amritk/runtime-validators 解决本地问题——因此整个文件信封是对照官方计划验证的,没有捆绑或方言引擎,而Schema Object内部则保持放任.
$ref 解析保留了调用者的工作:预设不拉入解析器,因此对于需要调用文档的规则(resolved: true通过一个 resolve 函数到核心 lintWithResult (例如包装) @amritk/resolve-refs) (中文(简体) ). 这个 mjst lint CLI已经电线了.
AsyncAPI 规则集
对事件驱动的API的相同处理. 这个 @amritk/lint/rules/asyncapi 子路径舰载一个预设覆盖 AsyncAPI 2.0–2.6 和 3.0 数据,在同一引擎上分层并添加 无依赖关系。 。 。 .
import { lint } from '@amritk/lint'
import { createAsyncApiRuleset } from '@amritk/lint/rules/asyncapi'
// Defaults to `extends: [asyncapi]` (recommended rules only, like `spectral:asyncapi`).
const ruleset = createAsyncApiRuleset()
const findings = await lint(document, { ruleset })
createAsyncApiRuleset(definition?, basePath?, options?) 镜像其 OpenAPI 对应器:内置层上分层的函数和格式检测器,以及 extends 决议 asyncapi 编号 : loupe:asyncapi 编号 : spectral:asyncapi 名称(最后两个如此现有的Spectral-tyle rulesets 扩展不变)。 启用每条规则 createAsyncApiRuleset({ extends: [['asyncapi', 'all']] })。 。 。 . options.restrictTo 应用相同的 信任边界。 。 。 .
| 导出 | 它会做什么 |
|---|---|
createAsyncApiRuleset(definition?, basePath?, options?) | 构建可运行的 AsyncAPI Ruleset (职能+格式+) extends 页:1. |
resolveAsyncApiRuleset(name, basePath?, options?) | 决定 extends 参考文献,包括 asyncapi 编号 : loupe:asyncapi 编号 : spectral:asyncapi 名称. |
asyncapi | 内置的AsyncAPI规则集定义. |
aasFunctions 编号 : allFunctions | A. AsyncAPI特定功能; allFunctions =内置+AsyncAPI. |
aasFormats | AsyncAPI 版本检测器(aas2, (中文(简体) ), aas2.0– 说吧 –aas2.6, (中文(简体) ), aas3, (中文(简体) ), aas3.0) (中文(简体) ). |
loadAsyncApiSchema(version) | Lazily 加载一个版本的官方结构元-schema('2.0'– 说吧 –'2.6', (中文(简体) ), '3.0') (中文(简体) ). 见 schemas/README.md。 。 。 . |
ASYNCAPI_VERSIONS 编号 : LATEST_ASYNCAPI_VERSION 编号 : asyncApiSchemaVersion(declared) | 捆绑的版本,最新的版本, 和从文档的映射 asyncapi 给其计划覆盖的未成年人的'2.6.4' → '2.6';未捆绑的未成年人 undefined) (中文(简体) ). |
两种主要内容都涵盖在内,规则按格式门接,所以一个 2.x 文档从不取出 3.x 规则. 有3.0种特异性 asyncapi-3- 前缀, 匹配 Spectral 的名称这样 .spectral.yml 重温性 个人规则继续工作.
结构规则与 正式 asyncapi/spec-json-schemas 以原价出售的元计价器 .json (单位:千美元)schemas/) (中文(简体) ). 这些是07号草案的文件,它们绝对地参照了自己的子计划 $id URI而不是由JSON Pointer(英语:JSON Pointer)——没有取出任何东西,因为每个目标都在同一文件内宣布. 每个文件都是上游字节的字节,除了 三个正则表达式,其中之一在失败的输入上是真正指数化的;每个输入被一个可证明的等同模式所取而代之的,有一个明确的限定词,测试套件主张对所生成的元件具有等同性. 见 计划读取。 。 。 .
结构验证运行 每份文件一次,对照书面文件 (单位:千美元)resolved: false,匹配 oas*-schema 以上规则。 这是一个刻意的选择,而不是监督:验证被贬低的树,而不是重新检查每一个树 components 输入一次 $ref 因此,一个重复使用的信息中的一个错误 三次报告在一个使用它两次的文件中。 取舍是怎样的 $ref 隐藏 : 从另一个文件中提取的内容没有进行结构检查, 也不存在指向错误对象类型的相同文件引用( a) messages 旨在 components.schemas,说来)——参考物本身造型良好,只有被贬低的树才会显示出不匹配. 这是OpenAPI预设的相同漏洞. 所有写在文件本身, components 包括,验证一次.
基准
这个 bench/ 套间坑 @amritk/lint 头对头对面 光谱 —— OpenAPI 插入此套件的模版(因此 spectral:oas 在现实世界中,测试套接字: 斯瓦格的宠物店 DigitalOcean API,和OpenAI API,横跨一个小配置和一个真正的大文档. 两者都做同样的工作: 内部参考 $refs 运行其推荐的 OpenAPI 规则集 (mjst在记忆中比照 @amritk/resolve-refs,与CLI完全一样; Spectral 使用自己的默认解析器). 运行它 bun run bench (本)或(本) bun run bench:node (节点).
@amritk/lint 在两个运行时间上,每个文档的完成速度比Spectral快数倍. 在节点上铅体更小,因为Spectral在V8上运行在物质上比JavaScriptCore快,而这种linter甚至接近于两者之间.
每个数字都是一整通的墙壁时间—— 每个规则,不是子集 ——以活字活字为主:JSONPath相配,规则功能,和去引用通关. 在两侧的每个迭代上都解析出一个新文档,以匹配工具的实际名称. 调查结果不同,因为两个规则不是字节相同的(不同的规则执行和 $ref 因此,这是一个 吞吐量 比较而不是正确性等同检查——但是在宠物店,两者都在同一结论上。 Spectral的JSONPath引擎 (英语:nimma) 在一些 Bun 版本下抛出在 OpenAI 的 spec 上,所以板凳上报告该侧为 errored 而不是中止运行.
大型文档. bun run bench:large <document.json> 乘以一个线粒体自行运行的每个阶段—— 由源头映射的剖面, JSONPath 规则, 手写行走进行相同的检查, lint(text) 反对 lint({ data, text })预设 $refs已解决——对照任何大型的OpenAPI文档(或者在没有给出时由固定装置所建的合成文件),并给结果打指印,以便可以检查两个运行是否输出相同.
规则集 被分开计时, 因为一个进程一次支付, 然后提示许多文档 : createOpenApiRuleset (将每个规则的JSONPath和接线功能和格式探测器合并)比 new Spectral() + 键 setRuleset(oas) 在两个运行时间.
许可证
麻省理工学院