@ effect/ yaml
YAML 1.2 解析,编辑,格式化和林入表达为效果计和纯函数. 将单个文档或多文档流分解为平方值或偏移保留 AST, 解析锁定和别名, 读取并保存每个节点的评论, 条评论, 计算字节-最小编辑, 格式, 通过路径修改, 将文档行走为 Stream aST 事件或定位符,将其与带有外科自定义的规则目录相对照——从您已有的文件中推断出指针配置——并直接解码成一个有效的域名图案.
预
1.0.0。 。 。 . 此软件包是@effected/*在稳定基础上建造的工具包 效应v4(effect~4.1.0) 并仍在0.x发展。 稳定效果 制作一个工具箱1.0.0可能,不是自动的。 为了保持你的effect财务报告和审定财务报表@effect/*该工具包在线上构建和测试、安装@effected/pnpm-plugin-effected。 。 。 .稳定:不稳定. 此软件包的 API 表面尚未考虑 完成并可能改变
0.x释放 定一个确切的版本——甚至 标记软件包 稳定 在此之前1.0.0可以引入突破性更改 和精确的针 变成一个类型检查错误 而不是一个 跑步时间惊喜。 完全政策: 释放策略。 。 。 .
为什么 @ effect/ yaml
YAML是未经信任的文本满足生产系统的地方:CI 管道定义,Kubernetes表单和配置文件,它们来自拉出请求,API有效载荷或用户主目录. 格式也足够大,一个剖面器有真正的攻击表面. 参照一主播的一主播——"十亿取笑""炸弹"——是数百字节的YAML扩展为千兆节的节点,而一深巢相通的流集是数千字节,会溢出一叠递归-去向-分解的相通.
这个一揽子方案将此视为头等舱要求,而不是脚注。 一个别名-扩展预算将实际化的节点数所限定,一个深度封顶会将收集的嵌入到剖面和字符串侧上,同时将两者都放入打出的错误通道. 敌对投入产生a YamlParseError 搭载结构化 YamlDiagnostic 有代码和位置的值; 它从未产生 RangeError无所处理的缺陷 或累累的堆积.
余从同出所出. 分析从错误中恢复,将每个诊断结果汇总为一次失败,而不是推倒第一次失败. 修改是按原字节进行编辑计算,因此注释和布局在变化后幸存. 所有东西都是纯粹的功能或计划:没有IO,没有服务,除了运行时间依赖之外,没有运行时间依赖 effect 和无依赖者 @effected/source (每个报告位置的行索引出自)——将Lexer, CST parser,作曲家和弦器以归属性而不是作为依赖性来销售入包. 它是Repo中最大的包, 它通过拥有它的引擎来赚取.
安装
npm install @effected/yaml effect
pnpm add @effected/yaml effect
需要节点.js~~24.11.0. effect v4 是一个对等依赖; 软件包本身没有添加其它运行时间依赖 .
全体 @effected/* 软件包仅为无害环境管理:只公布出口地图 import 条件,所以 require() 包括解析 CJS 模式的工具 —— 与节点失败 ERR_PACKAGE_PATH_NOT_EXPORTED 而不是装入不存在的 CJS 建筑。 从ES模块导入.
快开始
准备你的计划 Yaml.schema 将 YAML 直接解码为已验证域值 :
import { Yaml } from "@effected/yaml";
import { Effect, Schema } from "effect";
const Config = Schema.Struct({ port: Schema.Number });
const ConfigFromYaml = Yaml.schema(Config);
const program = Effect.gen(function* () {
return yield* Schema.decodeUnknownEffect(ConfigFromYaml)("port: 3000 # dev server");
});
Effect.runPromise(program).then(console.log);
// { port: 3000 }
Yaml.stringify 而不是扔出:
import { Yaml } from "@effected/yaml";
import { Effect } from "effect";
Effect.runPromise(Yaml.stringify({ port: 3000, hosts: ["a", "b"] })).then(console.log);
// port: 3000
// hosts:
// - a
// - b
两个方向都有一个同步对应的返回 a Result 对于不能等待效应的召唤者 vitest.config.ts 成为激励性案例. Yaml.parse 定义如下: Yaml.parseResult,因此两种表格不能对文档的含义产生分歧:
import { Yaml } from "@effected/yaml";
import { Result } from "effect";
const result = Yaml.parseResult("port: 3000 # dev server");
console.log(Result.isSuccess(result) ? result.success : result.failure);
// { port: 3000 }
输入失败
一种别名炸弹——巢起锚地,其扩张在每一个级别上都倍增——是以扩张预算和表面为界的 YamlParseError,不是作为非记忆性杀人:
import { Yaml } from "@effected/yaml";
import { Effect } from "effect";
const bomb = [
"a: &a [x,x,x,x,x,x,x,x,x]",
"b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]",
// ...further levels, each multiplying the one before
"z: [*g,*g,*g,*g,*g,*g,*g,*g,*g]",
].join("\n");
Effect.runPromise(Effect.result(Yaml.parse(bomb))).then(console.log);
// Failure with YamlParseError, whose `diagnostics` carry:
// { code: "AliasCountExceeded", message: "Alias expansion exceeded budget of ... nodes" }
收集的巢穴通过深度盖 行为相同,产生 NestingDepthExceeded 诊断而不是堆积溢出,以及 Yaml.stringify 将一个值编码回文本时,将镜像重现。 各地的保证都是一样的:每个倒闭的入站点都带有真正的出错通道,没有任何东西作为缺陷到达你的进程.
注释在编辑后幸存
人类维护的YAML文件大多是解释,解释是圆通通常丢失的部分. YamlFormat 对照原字节计算其编辑,从而更改一个值重写值并留下标题块,后缀 # LTS空行和缩进的位置 设置 preserveComments: false 这就是你要求另一种行为:
import { YamlFormat, YamlFormattingOptions } from "@effected/yaml";
const source = 'node: "20.11.0" # LTS\n\n# why the port\nport: 3000\n';
console.log(YamlFormat.formatToString(source));
// node: "20.11.0" # LTS
//
// # why the port
// port: 3000
console.log(YamlFormat.formatToString(source, undefined, YamlFormattingOptions.make({ preserveComments: false })));
// node: "20.11.0"
// port: 3000
评论也可作为数据读取。 每个节点级—— YamlScalar, (中文(简体) ), YamlMap, (中文(简体) ), YamlSeq 财务报告和审定财务报表 YamlAlias - 携带 commentBefore (节点上方的自有线路块), comment (严格地说,节点本身的行的后缀评论)和 spaceBefore (空行来先. YamlPair 不携带任何物品: key 联 合 国 value,而评论则在这两个节点上直播. YamlDocument 使用与节点相同的两个名称, commentBefore 给坐在前面的头条块 --- 标记和 comment 为后行道 YamlVisitor 标记 Comment 事件为 placement: "leading" | "trailing"。 。 。 .
标注的节点会遵守一条规则 条目上方的自行注释引导条目的密钥节点, 设置 commentBefore 和——一行空行来先—— spaceBefore。一个后继注释属于其线上最后一个节点: a: 1 # t 值结束行,所以 # t 值为 comment; 内 push: # only main 下面的值,所以注释是密钥的。 每个条目有两个插槽,而不是一个 push: # only main 和上方的自行注释 双回行字节- intact, 而不是一个拼写重写到另一个。 无标题块 --- 标记将其从项目流中分离出来,按照同一规则引导第一个条目的密钥;在标记引导根节点之后,一个.
林特和自定义
YamlLint.run 对照规则目录检查字符串并返回定位诊断; YamlLint.fix 应用能够以外科编辑方式表达的校正,并返回固定文本。 两者都是纯正而同步的. 这是线粒体引擎,不是跑取器——没有文件发现,没有配置文件加载,没有写入磁盘:
import { YamlLint, YamlLintConfig } from "@effected/yaml";
import { Result } from "effect";
const source = "# pinned\nname: demo \nport: yes\n";
for (const d of YamlLint.run(source, YamlLint.builtins, YamlLintConfig.default)) {
console.log(`${d.line}:${d.character} ${d.severity} ${d.rule} ${d.message}`);
}
// 1:5 error colon-spacing Too many spaces after ":" (2 > 1)
// 1:11 error trailing-spaces Trailing whitespace
// 2:6 error truthy Truthy value "yes" is not in the allowed spellings
const fixed = YamlLint.fix(source, YamlLint.builtins, YamlLintConfig.default);
console.log(Result.isSuccess(fixed) ? fixed.success : "fatal parse error — not safely fixable");
// # pinned
// name: demo
// port: "yes"
位置全以零为主,所以上面的第一个发现坐落在源的第二行. 修复路线 YamlEdit.applyAll 而不是重塑,所以自动固定不能移动一个它不被要求去触碰的评论,以及一个其修复需要重新流出文件船而根本不需要固定的规则. YamlLintConfig.default 财务报告和审定财务报表 YamlLintConfig.relaxed 是预设的;通过传播预设的来自编曲 rules 输入 YamlLintConfig.make配置验证是内置目录的规则意识——内置规则上拼错的选项密钥,或者试图关闭常入目录 parse-validity 规则失败了计划验证而不是被悄悄地忽略——而未知的(习惯)规则id通过时,其选项被视作不透明,留给习惯规则本身来验证. 自定义规则是纯数组连接—— YamlLint.run(text, [...YamlLint.builtins, myRule], config) ——并配置 myRule 在那个配置中,正是因为通过.
一个没有线条配置的重播可以推断出它的文件已经做什么了 . YamlLint.inferLenient 观察一个文本的风格——引用类型,缩进宽度,文档标记和其余部分——并返回主导风格所隐含的配置,外加剩余部分:在同一个文本上仍然产生配置的诊断.
import { YamlLint } from "@effected/yaml";
const workflow = ["---", "name: ci", "on:", " push:", " branches: ['main']", "jobs:", " build:", " runs-on: ubuntu-latest", ""].join("\n");
const { config, residual } = YamlLint.inferLenient(workflow, YamlLint.builtins);
console.log(config.rules["quoted-strings"], config.rules["document-start"], config.rules.indentation);
// { quoteType: 'single' } { present: true } { spaces: 2 }
for (const d of residual) console.log(`${d.line}:${d.character} ${d.rule} ${d.message}`);
// 2:0 truthy Truthy value "on" is not in the allowed spellings
YamlLint.inferStrict 同一步骤 Result 窗体: 结构化失败 YamlStyleConflictError ——当被观察到的风格与自己有分歧时,命名出每一个相冲突的维度,所有被观察到的拼写,计数和位置,而不是选择一个取胜者. 在很多文件中,写出原始的而不是方便的: YamlLint.observe 每个文件,合并 StyleEvidence 数值为 StyleEvidence.combine (证据是单一的,所以命令无关紧要) YamlLint.resolveStrict 或者说 YamlLint.resolveLenient。从未观察到的尺寸会回落到基础配置中而不是失败。 政策规则没有可察觉的风格—— truthy, (中文(简体) ), key-duplicates ——坚持以默认为驱动,如: line-length 财务报告和审定财务报表 empty-lines,其观测结果只有地平线:最长的一行证明极限至少是那么长,而不是它应该是什么,因此证据被携带,但从未被解为配置. 习惯规则通过执行可选 infer 钩上 YamlRule。 。 。 .
从 Pretier 移走
YamlFormat.formatToString(text, undefined, options) 替换 a prettier --write 超过 YAML 文件 。 选项映射 :
| 高级 | YamlFormattingOptions |
|---|---|
tabWidth | indent |
singleQuote | quoteStyle: "single" | "double" + 键 requoteScalars: true |
| 缩入的块序列( 总是在) | indentSequences: true |
printWidth 编号 : proseWrap | 无模拟——见下文 |
| 评论(最佳效果) | preserveComments: true (默认) |
两个默认与Pretier的输出不同,值得明确设定. 字符串默认为 quoteStyle: "single" 与工具箱的后遗症格式的字节相匹配,所以如果你运行 Prettier 时有其自身的默认(singleQuote: false),设置 quoteStyle: "double"。同样 indentSequences 默认为 false - 平地遗产形式与 - 键栏上的标记——而Pretier总是缩入一阶的块序列,所以前Pretier消费者几乎肯定想要 indentSequences: true。 。 。 .
这个 requoteScalars: true 半数 singleQuote 排行是购买完全等价的东西。 默认, quoteStyle 格式路径上仅引用字符串符 介绍 ——您来源中已经引用的 scalar 保留了自己的引文样式,所以格式化 a: 'x' 与 quoteStyle: "double" 叶子 'x' 毫无动静,Pretier会把它重写到 "x"与 requoteScalars: true (默认) false, (中文(简体) ), quoteStyle 适用于已经引用的源分解。 重引用只有在可以证明保存被解析出值时才会发生:单-双应用适当的双引出,而双-单在数值带有字符单引出不能表达时被跳出. 平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平 需要观看的一次互动: quoteStyle 省略默认为 "single"这样 requoteScalars: true 自行转换 "x" 改为 'x' - 一个前高级的消费者,他与 singleQuote: false 想要吗 quoteStyle: "double" 并肩作战.
printWidth 财务报告和审定财务报表 proseWrap 这里没有模拟 : lineWidth 折叠仅仅是一个数值路径特性( q)Yaml.stringify 编号 : Yaml.stringifyResult)),且该选项通过合同故意在格式路径上惰性—— YamlFormat 永不回流 scalar 内容( lineWidth 打开TSDoc YamlStringifyOptions 针这个。 Pretier 处理得最好的评论, 默认情况下完全保留下来.
多文件流——一个库伯涅特表,或两个文件 pnpm-lock.yaml pnpm 11 写道: configDependencies 整个格式:每份文件都按自己的顺序重新发送 ---编号 :... 框架和评论,所以从未放弃任何文件。 文件检测是CST级,所以 --- 在块 scalar 或引用的字符串中,通常为内容和格式。 格式会留下输入字节- 相同, 而不是在两种情况下损坏它 : 任何文档中一个致命的剖析错误, 和一条流 %YAML编号 :%TAG 指令(发射者不能忠实地再发射)。 验证整条流 Yaml.parseAllResult,当任何文件带有致命诊断时,其键入失败; YamlFormat.modify 保留单一文档,因为路径名称没有特定流文档.
为YAML 1.1 消费者代理
YAML 1.2的Core Schema解析平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平地平 一个平地 on 或者说 yes 圆圈作为本套件自己的解析器下的字符串,但成为了YAML 1.1消费者(js-yaml, PyYAML, libyaml, the)的布尔 yaml npm包的YAML 1.1 schema,大多数CI系统本土的YAML支持——读回:"挪威问题". quoteCompat: "yaml-1.1" 在出去的路上缩小了这一缺口,引用了每个平面平面平面的1.1个解析器会重新打出:扩展的布林克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克y编号 :yes编号 :no编号 :on编号 :off ) 1.1乘以印章、性别指数(1:30),下划线组合数字(1_000)和正辛/己/二进制整数形式(0777, (中文(简体) ), 0x_FF, (中文(简体) ), 0b1010_0111) (中文(简体) ). 它严格来说是添加剂——1.2规则已经引用过的任何内容都不会丢失一个引号,清单之外的任何字符串都得不到一个——用它来编曲 quoteStyle 引用字符和 lineWidth 用于折叠 :
import { Yaml, YamlStringifyOptions } from "@effected/yaml";
import { Effect } from "effect";
const frontmatter = { node: "on", released: "2024-01-15", port: "yes" };
const options = YamlStringifyOptions.make({ lineWidth: 0, quoteCompat: "yaml-1.1", quoteStyle: "double" });
Effect.runPromise(Yaml.stringify(frontmatter, options)).then(console.log);
// node: "on"
// released: "2024-01-15"
// port: "yes"
那个组合.. lineWidth: 0 (不折叠), quoteCompat: "yaml-1.1", (中文(简体) ), quoteStyle: "double" ——是指定用于YAML-1.1工具的字符串前置物的形状: Markdown前置物解析器和大多数静态站点生成器仍然在旧规则上解决. 此选项适用于两个值路径(Yaml.stringify, (中文(简体) ), Yaml.stringifyResult)和节点路径(YamlDocument#stringify, (中文(简体) ), YamlFormat),而带有明确标记的平板电脑则可以免用,就像它延伸的1.2型冲突检查一样.
特征
Yaml.parse编号 :Yaml.parseAll- 核对单一文件或文件的误差---- 分离成平面值,解决锚和别名,将每个诊断结果汇总成一个YamlParseError。 。 。 .Yaml.stringify—— 将纯值序列化回 YAML, 以失败键入YamlStringifyError圆形参照物或过深的筑巢; 块状映射键太长,无法使一行上溢出物形成明确的键状(? key编号 :: value)如此严格的解析器仍然接受输出;quoteCompat: "yaml-1.1"附加引用平面平面标注的 YAML 1.1 解析器将重排.Yaml.parseResult编号 :Yaml.parseAllResult编号 :Yaml.stringifyResult- 纯同步对应方,返回aResult而不是一个Effect用于无法等待的配置时调用器; 每个调用器都运行与其相同的引擎调用器Effect变体,因此两种形式不能相去甚远,致命的诊断,重复的密钥,别名炸弹或圆形参考物仍然无法打出而不是扔出.parseAllResult双向全流有效性检查 : 当流中的任何文档无效时, 它会失败 .Yaml.stripComments——引文-知评去除能使行数稳定,或者每个字节在给定替换字符时相抵稳定.Yaml.equals编号 :Yaml.equalsValue——语义等同,忽略了注释,白相间,格式化和映射密钥顺序,同时保持序列顺序显著.Yaml.schema编号 :Yaml.fromString编号 :Yaml.YamlFromString编号 :Yaml.allFromString将单一文件或整条流解码成验证效果的域计划工厂Schema数值.YamlNode——冲抵保留AST(一YamlScalar, (中文(简体) ),YamlMap, (中文(简体) ),YamlSeq, (中文(简体) ),YamlPair, (中文(简体) ),YamlAlias用于按位置或路径定位和读取节点;除此之外,每个节点类别YamlPair自己携带commentBefore, (中文(简体) ),comment财务报告和审定财务报表spaceBefore,一对只是它的key财务报告和审定财务报表value。 。 。 .YamlDocument——全解析AST外加已取出errors财务报告和审定财务报表warnings数组,所以部分有效的文档仍然可以检查,标题和后端的注释块为commentBefore编号 :comment,节点类所用的同两个名称.YamlEdit编号 :YamlFormat——计算用于格式化和基于路径的修改的字节-最小编辑数组,因此一个更改保留了您没有触及的每个注释和字节;preserveComments: false选择退出并选择进入requoteScalars应用quoteStyle用于源中已经引用的刻度、语义保留或跳出.format处理整个多文件流,重新发布每一文件及其框架;modify按合同是单行文件(一个路径名,没有特定流文件),但无法输入MultiDocumentStream而不是猜测.YamlVisitor——将解析后的文件作为Stream标记的事件加盟Stream.take大量投入提前终止aplacement每次评论活动.YamlToken编号 :YamlTokens——被定位的符号流, 提示规则为:tokenize是一个同步Result总是成功的 表面的词汇问题error- 礼物,和stream生成效果Stream从同一行走.YamlLint—— 说run, (中文(简体) ),fix编辑builtins目录:14个规则,包括解析有效性,行长,后行白空,空行,文件端新行,文档标记,重复键,引用,真实拼写,注释和结肠和连字符间距,并缩进. 加上通过现有法则的推断:observe生成可合并StyleEvidence, (中文(简体) ),resolveStrict输入失败YamlStyleConflictError以互相矛盾的拼写,resolveLenient选择主导风格inferStrict编号 :inferLenient一步一步每个文本.YamlLintConfig编号 :YamlLintRule编号 :YamlLintDiagnostic- 那个default财务报告和审定财务报表relaxed预设,一个针对规则目录验证的配置方案,以及一个自定义规则执行的模型,包括可选infer钩使自定义规则参与配置推论.YamlParseError编号 :YamlStringifyError编号 :YamlModificationError- 标有结构、位置的错误YamlDiagnostic数组而不是不透明消息.