@ 受影响/ package-json
packet.json 解析,编辑,验证和文件 IO 作为Effect schemas. Package 是一个 Schema.Class 上面有已知的字段 name 这是一个品牌npm名称, version 是真实的 SemVer, (中文(简体) ), packageManager 解码为 { name, version, integrity } ——和a rest 将每个未知的顶级密钥通过一个读取,编辑和写入周期,而不丢失的call-ll. 编辑是不可改变和双重签名的,验证是您可以替换的规则集 catalog: 编号 : workspace: 规格通过 @effected/npm 解析器合同是您选择加入的明确步骤.
预
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可以引入突破性更改 和精确的针 变成一个类型检查错误 而不是一个 跑步时间惊喜。 完全政策: 释放策略。 。 。 .
为什么
重写软件包的工具 Record<string, unknown>:读取,变异一键, JSON.stringify 它回来了。 这是正确的 直到它没有。 未知的钥匙通过意外而不是设计存活下来, version 是您比较的字符串 <,而当某人的表单有一个字段 你的类型从来没有模型 是一天,你发现 你的写路径保存它。 另一种方案是:严格规划,而不是 已经知道 字段——通常通过删除每个人的数据来解决打字问题.
这个包裹拒绝了两者。 已知的田地被打字和验证;其他所有土地都位于 rest 在编码上被平接回顶层密钥, 所以磁盘形状不会长出字元 rest 键并永不丢失 customTool 块。 串行化应用 Cononic sort-package-json 键式顺序,将依赖性地图按字母顺序排列,并条取出空地图,确定在本地和本地独立. PackageJsonFile.write 不要默默地解决你 workspace: 外出时的描述者,因为悄悄地重写你依赖值的写作不是写作,而是政策——所以 Package.resolve 这是你刻意写作的一步 而每一个明显的失败都有自己的标记:一个缺少的文件,一个无法读取的文件,一个无效的JSON和一个不能满足计划的文件,是四个不同的问题,四个不同的回收.
安装
npm install @effected/package-json effect @effect/platform-node
pnpm add @effected/package-json effect @effect/platform-node
需要节点.js~~24.11.0.
全体 @effected/* 软件包仅为无害环境管理:只公布出口地图 import 条件,所以 require() 包括解析 CJS 模式的工具 —— 与节点失败 ERR_PACKAGE_PATH_NOT_EXPORTED 而不是装入不存在的 CJS 建筑。 从ES模块导入.
effect v4是唯一的对等依赖. @effected/semver, (中文(简体) ), @effected/npm 财务报告和审定财务报表 @effected/files 作为普通的依附者,他们支持 version 字段、解析器合同和原子书写——以及 spdx-expression-parse 是树上的一个外部包,用于验证SPDX许可证表达式.
读取和写入文件需要 FileSystem (单位:千美元) Path 执行,一旦处于边缘状态,即 @effect/platform-node 在节点上。 除了别的事 PackageJsonFile 纯净,不需要任何平台层.
快开始
解码一个清单,编辑它,将计算出来的属性读回:
import { Package } from "@effected/package-json";
import { Effect } from "effect";
const program = Effect.gen(function* () {
const pkg = yield* Package.decode({ name: "@acme/widget", version: "1.0.0", private: true });
const next = yield* Package.setVersion(pkg, "1.1.0");
return [next.name, next.version.toString(), next.isScoped, next.isPrivate] as const;
});
console.log(Effect.runSync(program));
// => ["@acme/widget", "1.1.0", true, true]
next.version 是一个 SemVer,不是字符串,所以你比较一下 next.version.gt(other) 并把它与 version.bump.minor() 而不是达到一个regex.
软件包模式
编辑返回新 Package. 变相静电是双重的,所以 Package.addDependency(pkg, "effect", "^4.0.0") 财务报告和审定财务报表 pkg.pipe(Package.addDependency("effect", "^4.0.0")) 是同一个电话。 那些能失败的.. setVersion, (中文(简体) ), setName, (中文(简体) ), setLicense 返回一个 Effect 带有相应的打出错误,其余为平面函数.
未知密钥回转 . toJsonString 编码通过电线编码器、平板 rest 返回到顶层,并应用条形键顺序 :
import { Package } from "@effected/package-json";
import { Effect } from "effect";
const program = Effect.gen(function* () {
const pkg = yield* Package.decode({
name: "widget",
version: "1.0.0",
scripts: { build: "tsc" },
customTool: { flag: true },
});
return Package.addDevDependency(pkg, "typescript", "^6.0.0").toJsonString();
});
console.log(Effect.runSync(program));
// {
// "name": "widget",
// "version": "1.0.0",
// "scripts": {
// "build": "tsc"
// },
// "devDependencies": {
// "typescript": "^6.0.0"
// },
// "customTool": {
// "flag": true
// }
// }
toJsonString 带 PackageFormatOptions —— 说 indent, (中文(简体) ), sort, (中文(简体) ), stripEmpty, (中文(简体) ), newline - 如果你想要生的形状 而不是犬科的.
边上 Package 叶子概念可以自己使用. PackageName 分类和验证 npm 名称 (isValid, (中文(简体) ), isScoped, (中文(简体) ), scope, (中文(简体) ), unscoped并标出他们 ScopedPackageName 或者说 UnscopedPackageName。 。 。 . DependencySpecifier 将任意指定字符串分类为一个协议—— range, (中文(简体) ), tag, (中文(简体) ), git, (中文(简体) ), url, (中文(简体) ), npm, (中文(简体) ), file, (中文(简体) ), link, (中文(简体) ), portal, (中文(简体) ), catalog, (中文(简体) ), workspace 或者说 unknown 区域大小写 Range 从 @effected/semver。 。 。 . Dependency 配对带有指定符号的名称和 kind 它来自地图, 暴露了同样的协议上游 与获取者.
阅读和写作
PackageJsonFile 是包中唯一的IO: 一个服务,两个方法,在核心之上 FileSystem 财务报告和审定财务报表 Path。 。 。 .
import { Package, PackageJsonFile } from "@effected/package-json";
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
import * as NodePath from "@effect/platform-node/NodePath";
import { Effect, Layer } from "effect";
const bumpMinor = Effect.gen(function* () {
const files = yield* PackageJsonFile;
const pkg = yield* files.read("./package.json");
const next = pkg.copyWith({ version: pkg.version.bump.minor() });
yield* files.write("./package.json", next);
});
const PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);
Effect.runPromise(bumpMinor.pipe(Effect.provide(PackageJsonFile.layer), Effect.provide(PlatformLive)));
read 失败了四种不同的方式,并说: PackageJsonNotFoundError 当文件不在时, PackageJsonReadError 对于其他文件系统故障, PackageJsonParseError 当字节不是JSON时 PackageDecodeError 当JSON不是包裹。 没有 exists 预选,因此在检查和读取之间删除的文件不能被误报为 IO 错误.
验证
PackageValidator 在解码器上运行一个设置的规则 Package 总计 每个 失败到一个 PackageValidationError而不是第一时间停下来.
import { Package, PackageValidator } from "@effected/package-json";
import { Effect } from "effect";
const program = Effect.gen(function* () {
const pkg = yield* Package.decode({ name: "widget", version: "1.0.0" });
const validator = yield* PackageValidator;
return yield* validator.validate(pkg);
}).pipe(
Effect.provide(PackageValidator.layer),
Effect.catchTag("PackageValidationError", (error) => Effect.succeed(error.failures.map((failure) => failure.rule))),
);
console.log(Effect.runSync(program));
// => ["has-license", "has-description", "has-repository"]
PackageValidator.layer 带有默认规则( e)has-license, (中文(简体) ), has-description, (中文(简体) ), has-repository, (中文(简体) ), not-private) (中文(简体) ). PackageValidator.layerRules({ rules }) 换成你自己的设定——a ValidationRule 是一个名称加一个无法使用 RuleFailure。两艘额外的规则船用于发布门: noUnresolvedDepsRule 失败于任何 workspace: 或者说 catalog: 标出在表里 noLocalDepsRule 失败 file:, (中文(简体) ), link: 财务报告和审定财务报表 portal:。 。 。 .
解析目录:和工作空间:规格
Package.resolve 扩展 catalog: 财务报告和审定财务报表 workspace: 横跨所有四个依赖性地图的示意图,使用 CatalogResolver 财务报告和审定财务报表 WorkspaceResolver 合同来源 @effected/npm这一一揽子方案蓄意不能执行,对工作空间没有看法,因此,执行是从上下文产生的. @effected/workspaces 提供真实的; 固定记录可以进行测试.
指定解析者的回答 None 因为他们是原样的.
import { CatalogResolver, WorkspaceResolver } from "@effected/npm";
import { Package } from "@effected/package-json";
import { Effect, HashMap, Layer, Option } from "effect";
const Resolvers = Layer.mergeAll(
Layer.succeed(CatalogResolver, { rangeOf: () => Effect.succeed(Option.some("^4.0.0")) }),
Layer.succeed(WorkspaceResolver, { versionOf: () => Effect.succeed(Option.some("1.4.0")) }),
);
const program = Effect.gen(function* () {
const pkg = yield* Package.decode({
name: "widget",
version: "1.0.0",
dependencies: { effect: "catalog:", "@acme/core": "workspace:^" },
});
const resolved = yield* Package.resolve(pkg);
return [
Option.getOrElse(HashMap.get(resolved.dependencies, "effect"), () => "unresolved"),
Option.getOrElse(HashMap.get(resolved.dependencies, "@acme/core"), () => "unresolved"),
] as const;
}).pipe(Effect.provide(Resolvers));
console.log(Effect.runSync(program));
// => ["^4.0.0", "^1.4.0"]
这个 workspace: 范围修改符被尊重 : workspace:* 取赤色的版本, workspace:^ 财务报告和审定财务报表 workspace:~ 前缀,并使用一个明确的修饰符。 预测是 @effected/npm是因为 DependencySpecifier 有完整 pnpm 的静态发布语义:别名形式 workspace:<name>@<range> 决定 目标 软件包的版本并成为 npm:<name>@<range> 别名 pnpm 发布,空白目录名称选择默认目录. 失败的目录组装表面键入为 @effected/npm是因为 CatalogAssemblyError,与合同一起" DependencyResolutionError。 。 。 .
亮度发现
Package.decode 财务报告和审定财务报表 PackageManifest 严格:一个字段不正确,整个文档都失败。 这是你们将要写或出版的表单的正确行为,而你们只嗅到的表单的不正确行为——取取出柏油球来 node_modules ——如果文档是别人的数据和一个糟糕的字段,则不应当沉入读取. LenientManifest 是该发现级: 每 Package 字段解码到其简单的允许 JSON 形状(name 财务报告和审定财务报表 version 接受任何字符串,而不是标记npm语法; license 接受任意字符串, 没有 SPDX 检查; 依赖性地图是纯记录, 不是 HashMap字段存在,但即使该形状也不退化为缺失,而不是文档失败,其原始价值在 rest (一个畸形的已知字段被完全视为未知字段),并报告了降解情况 issues数字 :
import { LenientManifest } from "@effected/package-json";
import { Effect } from "effect";
const program = Effect.gen(function* () {
const sniffed = yield* LenientManifest.decode({ name: "JSONStream", version: "1.0", license: 42 });
return [sniffed.name, sniffed.version, sniffed.issues, sniffed.rest?.license] as const;
});
console.log(Effect.runSync(program));
// [
// "JSONStream",
// "1.0",
// [{ field: "license", expected: "a string", value: 42 }],
// 42,
// ]
宽放是每个字段,从来不是每个语法: decodeResult编号 :decode 仍然无法输入 PackageDecodeError 当输入根本不是JSON对象时(null一个阵列,一个平面 parseResult编号 :parse - 处理生的一对 JSON.parse - 输入失败 PackageJsonSyntaxError 当文本无效或不分析对象时。 每对都遵循包件的通常形状: decodeResult编号 :parseResult 是同步的 Result 原始人们, decode编号 :parse 是他们的 Effect 带有跟踪跨度的表格.
一个空的 issues 数组不是一个有效性保证——允许形状检查JSON形状,而不是npm语义,所以 LenientManifest 没有问题仍然不能失败 Package.decode。 。 。 . LenientManifest 没有突变静态和写入路径; 一旦需要验证或编辑, 请重新解码 原文 通过输入 PackageManifest.decode 或 Package.decode (严格,可出版).
解决切入点
resolveEntryPoint 回答一个关于一个清单的问题,即: "." 输入 —— 它是纯净的, IO 自由的和 Result- 返回,所以它对一个没有磁盘包的平面物体起作用:
import { resolveEntryPoint } from "@effected/package-json";
resolveEntryPoint({ exports: { import: "./esm.js", require: "./cjs.js" } });
// Result.succeed("./esm.js")
resolveEntryPoint({ exports: { require: "./cjs.js" } }, { conditions: ["require"] });
// Result.succeed("./cjs.js")
resolveEntryPoint({ main: "./legacy.js" });
// Result.succeed("./legacy.js") — no exports field, so main applies
resolveEntryPoint({ exports: { require: "./cjs.js" }, main: "./legacy.js" });
// Result.fail(UnresolvedEntryPointError { reason: "noConditionMatched" })
所有三个法律 exports 拼写被尊重——字符串的快手,一个子路径图,以及根部的条件没有 "." 密钥——和默认条件 ["import", "default"],按优先顺序排列.
以上最后一例是值得了解的语义: exports 封装软件包,所以一个现成但无比 exports 是已键入的失败,并且 main 实值 没有 咨询。 这是Node的规矩 宽宏大量地读到 main,然后改为 index.js ——回答一个文件包故意不导出,它加载并行为合理而不是失败. 只有一个 缺席 exports 达到 main,然后是遗产 index.js 默认。 一个 exports 窗体解析器不执行(一个数组倒置列表,或者一个没有的子路径图) "." ) 失败的原因,而不是被猜测.
说对了 @effected/npm是因为 PackageTarball 在任何安装运行之前,在所提取的柏油球内找到条目文件.
解决任何子路径、导入和类型
PackageExports 是Node的解析器算法,以显示:任何子路径, # 进口, * 模式, null 排除、数组倒置和 TypeScript 读取相同字段。 每个静电都是同步的 Result 原始带 Effect 双胞胎 :
import { PackageExports } from "@effected/package-json";
import { Option, Result } from "effect";
const manifest = {
exports: {
".": { types: "./index.d.ts", import: "./index.js" },
"./*": { types: "./types/*.d.ts", import: "./dist/*.js" },
"./internal/*": null,
},
};
const http = PackageExports.resolveResult(manifest, "./Http");
console.log(Result.isSuccess(http) && http.success.path);
// => ./dist/Http.js
const hidden = PackageExports.resolveResult(manifest, "./internal/secret");
console.log(Result.isFailure(hidden) && hidden.failure.code);
// => ERR_PACKAGE_PATH_NOT_EXPORTED
const typed = PackageExports.resolveTypesResult(manifest, "./Http");
console.log(Result.isSuccess(typed) && Option.getOrNull(typed.success.declaration));
// => ./types/Http.d.ts
conditions 是一组,而不是优先列表: 显示器本身的密钥顺序决定, 如在节点和 TypeScript 中那样, 所以 a types 键写入后 import 永远无法到达. splitSpecifier("@scope/pkg/sub") 赐名和子路, entriesResult 财务报告和审定财务报表 typesEntriesResult 列出所有一揽子出口 PackageJsonFile.exportedFiles(directory, manifest) 根据磁盘上的文件扩展模式导出.
每个答案都留在包裹里 不从开始的目标 ./,或带有 .., (中文(简体) ), .时,空或 node_modules (百分比编码或不编码, \ 包含),未输入 PackageExportsError,则a * 匹配时也不能注入 : "./*": "./dist/*.js" 决定 ./a 并拒绝 ./%2e%2e/secret。 。 。 .
错误
每一次失败都是 Schema.TaggedError 路由为 Effect.catchTag在结构上保留原因 Schema.Defect 外地——a PackageDecodeError 拿着 SchemaError 问题树, 不 String(error)。 。 。 .
| 标记 | 手段 |
|---|---|
PackageJsonNotFoundError | 路径没有文件 。 通常不会出错:倒下,或者走起. |
PackageJsonReadError | 文件已存在, 无法读取 。 卡莉丝 path 结构 cause。 。 。 . |
PackageJsonParseError | 字节不是JSON. 卡莉丝 path 编辑 SyntaxError。 。 。 . |
PackageDecodeError | JSON不是包裹,json。 载着 SchemaError 以它的问题树命名. |
PackageJsonWriteError | 写入失败 。 缩小到文件系统失败, 绝不是编码错误 . |
PackageValidationError | 一个或多个验证规则失败 。 运出 failure,每个都有它的规则名称,消息和JSON路径. |
InvalidPackageNameError | 字符串不符合 npm 命名规则 。 升为 Package.setName。 。 。 . |
InvalidSpdxLicenseError | 字符串不是有效的 SPDX 许可证表达式 。 升为 Package.setLicense。 。 。 . |
InvalidDependencySpecifierError | 字符串不是公认的依赖性描述符 。 升为 DependencySpecifier.decode。 。 。 . |
UnresolvedEntryPointError | 任何切入点都无法从清单中解决。 回来的a Result 以 resolveEntryPoint,从未抚养,与 reason 告诉 noConditionMatched, (中文(简体) ), noRootExport 财务报告和审定财务报表 unsupportedExportsForm 分开. |
Package.setVersion 失败 InvalidVersionError 从 @effected/semver,这就是版本语法所居住的地方.
特征
Package——表型:已打入已知字段rest包罗万象,计算得分(isPrivate, (中文(简体) ),isScoped, (中文(简体) ),isESM, (中文(简体) ),hasDependency,四个get*Dependencies),双突变静态,copyWith, (中文(简体) ),Package.decode纯洁的toJsonString连载器.Package.schema编号 :Package.wireFor- 开放的JSON 级电线编码器, 以及制造一台用于.extend()ed 子类,所以它的自定义字段解码为打字成员而不是掉入rest。 。 。 .PackageJsonFile- IO表面:read财务报告和审定财务报表write超过核心FileSystem编号 :Path,在边缘提供平台执行.PackageValidator——基于规则的校验汇总每次失败,同时设置默认规则集,参数化layerRules和出版门规则noUnresolvedDepsRule财务报告和审定财务报表noLocalDepsRule。 。 。 .resolveEntryPoint纯洁的,Result- 将入境点解析器放回清单exports编号 :main,荣誉exports封装而不是掉到main,与EntryPointManifest作为它宽容的输入形状.LenientManifest- 下方的外形延展级PackageManifest: 已知字段不整齐地退化为缺失,在rest报告issues,而不是文件失败;decodeResult编号 :decode财务报告和审定财务报表parseResult编号 :parse在套件的常规中Result编号 :Effect相对.Package.resolve—— 说catalog:财务报告和审定财务报表workspace:经济、社会、文化权利委员会@effected/npm与pnpm的出版时间预测(别名包括表格)签订合同,作为明确步骤write从不为你表演.PackageName, (中文(简体) ),DependencySpecifier, (中文(简体) ),Dependency, (中文(简体) ),SpdxLicense, (中文(简体) ),PackageManager, (中文(简体) ),Person, (中文(简体) ),Repository, (中文(简体) ),Bugs, (中文(简体) ),Funding, (中文(简体) ),DevEngine——叶子概念,各自拥有自己的静态,品牌和出错,可以独立地使用Package。 。 。 .Repository财务报告和审定财务报表Bugs解码repository财务报告和审定财务报表bugs字段,从它们的快手或对象形式,同样的方式Person对author, (中文(简体) ),contributors财务报告和审定财务报表maintainers· ;Repository还揭露browseUrl财务报告和审定财务报表gitUrl将简指或 SSH 格式正规化的获取器https://。 。 。 .Repository.directoryUrl—— 由 :repository.directory在GitHub,GitLab和Bitbucket上反对东道主的路径大会.none对会议地点不详的东道国,或directory它从存储处爬出——一个被猜想的路径在寻找权威的同时,将一无所获地决断。 没有directory,这是browseUrl。 。 。 .Funding- 那个funding字段,从空的 URL 字符串、对象或其中之一的数组中解码,以及 永远 给一个数组,这样消费者就永远不要在分量上分化。 每个条目以它读取的形式重新编码,未识别的密钥存活在其中rest。 。 。 .licenseExpressionOfa 包括:SpdxLicense作为Option<SpdxExpression>从@effected/spdx对于一个想要解析表达而不是弦的呼叫者来说.none联 合 国SEE LICENSE IN …财务报告和审定财务报表UNLICENSED表格,这些表格是有效的license值,但不许可表达式.Package也类型keywords, (中文(简体) ),maintainers财务报告和审定财务报表homepage直接,与现有author财务报告和审定财务报表contributors。 。 。 .- 战地计划( E)
DependencyMapField, (中文(简体) ),BinField, (中文(简体) ),ExportsField, (中文(简体) ),PublishConfigField, (中文(简体) ),PeerDependenciesMetaField, (中文(简体) ),StringMapField)用于扩展模型的子类.RepositoryField已导出但已贬值 :Package.repository现在解码通过Repository.FromValuea. 绕行原短手或对象形式;RepositoryField仅供仍在原始结合中的消费者使用.