ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Effect 终端检测实战:通过 Stdio 服务使用 stdinIsTerminal 与 stdoutIsTerminal

Effect 终端检测实战:通过 Stdio 服务使用 stdinIsTerminal 与 stdoutIsTerminal Effect 终端检测实战通过 Stdio 服务使用 stdinIsTerminal 与 stdoutIsTerminal【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect本篇技术指南聚焦于 Effect 生态中Stdio服务的一项新增能力通过stdinIsTerminal与stdoutIsTerminal两个 Effect在类型安全的环境内判断标准输入/标准输出是否连接到终端TTY。文中将结合本仓库的源码实现、平台适配层与测试用例说明其接口定义、默认行为、Node.js 与 Deno 两种运行时的底层实现以及它在颜色输出、交互提示等场景中的实际应用帮助读者理解并直接落地这一能力。变更背景Stdio 服务新增终端检测 Effects本仓库的 changeset 文件 .changeset/pre/eff-532-stdio-terminal.md 记录了一次针对effect、effect/platform-node-shared、effect/platform-deno三个包的 patch 级变更ExposestdinIsTerminalandstdoutIsTerminaleffects through theStdioservice.这条变更的核心含义是把当前进程的标准输入/标准输出是否附着在终端上这一信息以 Effect 的形式暴露给开发者而不是让使用者直接读取全局的process.stdin.isTTY等运行时专有属性。与之对应的版本记录也出现在 packages/effect/CHANGELOG.md 中对应 PR #7090由 tim-smart 提交。之所以采用 Effect 而非普通布尔值是因为Stdio服务的设计原则是程序通过 Effect 环境依赖标准 I/O而不是直接触碰全局进程句柄。终端状态作为一种随运行环境变化的信息自然也应通过 Effect 延迟求值、按需读取。Stdio 服务接口终端状态如何建模Stdio服务契约定义在 packages/effect/src/Stdio.ts 中其核心接口包含五个成员成员类型说明argsEffect.EffectReadonlyArraystring命令行参数stdinIsTerminalEffect.Effectboolean标准输入是否连接到终端stdoutIsTerminalEffect.Effectboolean标准输出是否连接到终端stdout(options?)Sink标准输出 Sink接受字符串或字节stderr(options?)Sink标准错误 Sink接受字符串或字节stdinStreamUint8Array, PlatformError标准输入的字节流其中stdinIsTerminal与stdoutIsTerminal正是本次变更新增的两个成员均返回Effect.Effectboolean。它们被设计为 Effect 而非同步取值意味着终端状态是在 effect 执行时才被读取的——这一点在后面的测试用例中体现得尤为明显。I/O 相关操作可能失败并抛出PlatformError而终端检测这两个字段不会失败因此类型签名中错误通道为never。make 构造器与默认行为Stdio.makepackages/effect/src/Stdio.ts#L122-L131用于从字段组装服务实例它对终端检测字段的处理很关键如果调用方未提供stdinIsTerminal/stdoutIsTerminal默认值为Effect.succeed(false)。这意味着任何自定义实现的 Stdio 服务在未显式声明终端状态时会被保守地视为非终端这与大多数 CLI 库在无法判断时的回退策略一致。layerTest测试层的确定性覆盖Stdio.layerTest提供一个测试 Layer默认情况下参数为空数组、stdout/stderr 为排空 Sink、stdin 为空流、终端检测 effects 成功返回false。调用方可通过传入部分实现覆盖任意字段。测试用例 packages/effect/test/Stdio.test.ts 验证了这两点layerTest默认情况下stdinIsTerminal与stdoutIsTerminal均返回false传入stdinIsTerminal: Effect.succeed(true)、stdoutIsTerminal: Effect.succeed(true)后服务返回true。也就是说在单元测试中你可以完全掌控是否终端这一状态从而分别验证交互式与非交互式两条代码路径。运行时实现Node.js 与 Deno 的底层差异同一个服务接口在不同运行时下通过各自的原生 API 实现终端检测。Node.js基于 process 流的 isTTYeffect/platform-node-shared中的 NodeStdio.ts 提供了NodeStdio.layer其终端检测实现为stdinIsTerminal: Effect.sync(() process.stdin.isTTY true), stdoutIsTerminal: Effect.sync(() process.stdout.isTTY true)这里使用 true的严格比较是因为 Node.js 中stream.isTTY仅在流连接终端时才会被定义为布尔值true当流被重定向到文件或管道时该属性为undefinedundefined true得到false从而正确反映非终端状态。同时它使用Effect.sync延迟求值确保在 effect 实际运行时才读取属性而不是在 Layer 构建时就固化结果。Deno基于 isTerminal() 方法effect/platform-deno中的 DenoStdio.ts 实现则调用 Deno 原生 APIstdinIsTerminal: Effect.sync(() Deno.stdin.isTerminal()), stdoutIsTerminal: Effect.sync(() Deno.stdout.isTerminal())两者接口一致、语义一致只是底层探测机制不同——这正体现了通过Stdio服务抽象平台差异的价值业务代码无需感知运行时差异。测试如何验证读取时机Node.js 的测试 packages/platform/node-shared/test/NodeStdio.test.ts 通过Object.defineProperty动态改写process.stdin/process.stdout的isTTY属性然后分别断言设置为(stdin: true, stdout: false)时stdinIsTerminal返回true、stdoutIsTerminal返回false再改为(stdin: false, stdout: true)时结果随之反转。Deno 侧测试 packages/platform/deno/test/DenoStdio.test.ts 采用同样的模式改写Deno.stdin/Deno.stdout的isTerminal方法。两个测试共同证明了一个关键事实终端状态是在 effect 执行时动态读取的而不是在 Layer 初始化时快照的。这在长期运行的服务中尤其重要——例如进程在启动后被从后台任务前台化终端状态可能发生变化。实战场景基于终端状态做差异化行为获取终端状态后最常见的应用是让程序在交互式终端与重定向/管道环境下表现出不同行为场景一自动控制 ANSI 颜色输出本仓库的 CLI 模块 packages/effect/src/unstable/cli/CliOutput.ts#L298-L309 中的defaultFormatter展示了同样的判定思路当process.stdout.isTTY true且未设置NO_COLOR环境变量时启用 ANSI 颜色否则退化为纯文本。借助Stdio服务你可以把这种全局属性读取替换为类型安全的依赖注入import * as Effect from effect/Effect import * as Stdio from effect/Stdio const format Effect.gen(function*() { const stdio yield* Stdio.Stdio const useColor yield* stdio.stdoutIsTerminal return useColor ? \x1b[36mcyan\x1b[0m : cyan })当 stdout 被重定向到日志文件或 CI 管道时useColor自动为false避免向日志文件写入裸 ANSI 转义序列。场景二交互式提示与管道兼容类似地stdinIsTerminal可用于区分用户正在终端交互输入与数据通过管道流入import * as Effect from effect/Effect import * as Stdio from effect/Stdio const maybePrompt Effect.gen(function*() { const stdio yield* Stdio.Stdio if (yield* stdio.stdinIsTerminal) { // 交互式输出提示并等待用户输入 yield* stdio.stdout().pipe(/* ... */) } else { // 非交互式直接消费 stdin 流 yield* stdio.stdin.runForEach(/* ... */) } })这种模式让同一个 CLI 既能用于人机交互又能安全地参与管道组合如cat file | my-cli。组合使用与测试策略要使用该能力需要为程序提供Stdio服务Node.js 运行时Effect.provide(NodeStdio.layer)来自effect/platform-node-shared/NodeStdioDeno 运行时Effect.provide(DenoStdio.layer)来自effect/platform-deno/DenoStdio单元测试Effect.provide(Stdio.layerTest({ ... }))手动注入终端状态实现确定性断言。推荐的测试写法是对终端/非终端两个分支分别用layerTest覆盖不同状态并配合assert.isTrue/assert.isFalse断言参考 packages/effect/test/Stdio.test.ts 的断言风格从而保证颜色输出、交互提示等分支逻辑在 CI通常非 TTY环境下也能被完整测试。小结stdinIsTerminal与stdoutIsTerminal的加入补齐了Stdio服务在终端能力探测上的空白接口层以Effect.Effectboolean建模遵循 Stdio 服务通过环境依赖标准 I/O的设计哲学构造器与测试层对未提供值默认回退为false语义保守且可覆盖Node.jsisTTY true与 DenoisTerminal()两个平台实现接口一致、探测方式各异业务代码无需关心运行时差异测试用例证明终端状态在 effect 执行时动态读取支持运行期间状态变化。无论你是在开发 CLI 工具、颜色输出格式化还是需要构建交互式/管道式双模式程序都可以通过Stdio服务以类型安全、可测试的方式获取终端信息替代直接读取全局进程属性的传统做法。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表