ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Effect 数据库迁移(Migrator)Windows 文件路径兼容修复:`Migrator.fromFileSystem` 如何通过 File URL 让 ESM 加载器接受绝对路径

Effect 数据库迁移(Migrator)Windows 文件路径兼容修复:`Migrator.fromFileSystem` 如何通过 File URL 让 ESM 加载器接受绝对路径 Effect 数据库迁移MigratorWindows 文件路径兼容修复Migrator.fromFileSystem如何通过 File URL 让 ESM 加载器接受绝对路径【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect导读本文讲解 effect 仓库中一次针对 SQL 迁移工具的关键修复Migrator.fromFileSystem在加载迁移文件时不再把目录与文件名拼成普通路径直接传给import而是先通过Path服务的toFileUrl将路径解析为 file URL再交给 ESM 加载器。这一改动让 Windows 上的绝对路径如D:\migrations\1_init.ts可以被正常导入同时使fromFileSystem的类型要求从LoaderFileSystem变为LoaderFileSystem | Path。读完本文你将理解该问题的根因、修复的底层调用链、类型层面的影响以及如何为应用正确提供Pathlayer。说明本文基于 .changeset/pre/migrator-windows-file-url.md 展开结合 Migrator.ts 源码与 Migrator.test.ts 测试佐证。问题背景Windows 绝对路径被 ESM 加载器拒绝Effect 的 SQL 迁移工具位于 Migrator.ts支持从文件系统目录加载迁移。此前fromFileSystem将目录与文件名直接拼成普通路径字符串传给import。在 Windows 上这会生成形如D:\migrations\1_init.ts的模块说明符specifier而 ESM 加载器会拒绝它并抛出错误Only URLs with a scheme in: file, data, and node are supported也就是说ESM 加载器只接受带有file:、data:、node:scheme 的 URL或合法的相对/裸模块说明符而D:\migrations\1_init.ts这种带盘符与反斜杠的绝对路径既不是 URL 也不是合法说明符因此被拒绝。这是 Windows ESM 环境下运行fromFileSystem迁移的典型故障。修复方案先经Path服务解析为 File URL修复的核心思路是不要直接import普通路径而是先通过Path服务的toFileUrl把路径转换为 file URL再importURL 的href。在 Migrator.ts 中fromFileSystem的实现要点如下通过yield* FileSystem获取文件系统服务通过yield* Path获取路径服务用Fs.readDirectory(directory)读取迁移目录读取失败会映射为MigrationErrorkind 为Failed消息为Failed to read migrations directory用正则^(?:.*\/)?(\d)_([^.])\.(js|ts|mjs|mts)$解析文件名得到迁移 id 与名称忽略其他文件例如0005_ignored.cjs会被跳过详见测试 Migrator.test.ts对每个迁移文件先执行path.toFileUrl(path.join(directory, basename))得到 URL再import(url.href)完成动态导入。关键代码片段Migrator.tsonSome: ([basename, id, name]): ReadonlyArrayResolvedMigration [ [ Number(id), name, // import needs a file URL: on Windows an absolute path such as // D:\migrations\1_init.ts is rejected by the ESM loader. orDie keeps the // failure a defect so loadMigration reports it as an import error. Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) Effect.promise( () import( /* vite-ignore */ /* webpackIgnore: true */ url.href ) )) ] ] as const几点实现细节值得注意path.join由Path服务提供因此在 Windows 平台实现下会正确使用\连接路径段在测试中用一个模拟的 Windows layerjoin用\\拼接、toFileUrl返回预置 URL验证了最终解析出的路径正是C:\migrations\0001_first.jsMigrator.test.ts。Effect.orDie包裹toFileUrl当toFileUrl失败例如抛出BadArgument时把它作为缺陷defect而不是类型化错误抛出这样在迁移加载时会被loadMigration统一包装成MigrationErrorkind 为ImportError。测试 Migrator.test.ts 专门验证了这一行为让toFileUrl返回失败后加载结果以defect:开头而不是从make的错误通道MigrationError | SqlError逃逸。动态导入附带/* vite-ignore */与/* webpackIgnore: true */注释确保 Vite、webpack 等打包器不会静态解析或改写该动态导入。类型层面的影响LoaderFileSystem变为LoaderFileSystem | Path由于fromFileSystem现在需要读取Path服务其返回类型从LoaderFileSystem拓宽为LoaderFileSystem | Path见 Migrator.ts 的签名export const fromFileSystem: (directory: string) LoaderFileSystem | Path。这对调用方的 Layer 组装提出了新要求使用聚合平台层的调用方不受影响例如已经提供NodeServices.layerNode 平台聚合层包含FileSystem、Path、Clock、Random等一组服务的应用fromFileSystem所需的服务已经齐备无需改动。单独提供FileSystem的调用方需要新增Pathlayer如果之前只提供了FileSystem现在必须额外提供一个Pathlayer否则运行时会出现服务缺失错误。Windows 上必须使用平台感知的Path实现文档与源码注释都特别强调Windows 下不能使用核心库自带的 POSIX 语义的Path.layer因为它不保留 Windows 盘符路径见 Migrator.ts 的 JSDocthe corePath.layeruses POSIX semantics and does not preserve Windows drive-letter paths。核心Path.layer的 POSIX 局限核心库的 Path.ts 提供了Path.layerPOSIX 语义。其toFileUrl实现Path.ts先把路径resolve成 POSIX 绝对路径再编码为file://URLfunction toFileUrl(filepath: string) { const outURL new URL(file://) let resolved resolve(filepath) // path.resolve strips trailing slashes so we must add them back const filePathLast filepath.charCodeAt(filepath.length - 1) if ( (filePathLast CHAR_FORWARD_SLASH) resolved[resolved.length - 1] ! / ) { resolved / } outURL.pathname encodePathChars(resolved) return Effect.succeed(outURL) }encodePathChars还会把%、\、换行、回车、制表符等字符按 URL 编码规则转义Path.ts。对D:\migrations\1_init.ts这类 Windows 盘符路径POSIX 的resolve并不会把D:视为盘符因此生成的 URL 无法正确表达盘符语义——这正是文档要求 Windows 上使用平台感知实现的原因。Windows 上应使用NodePath.layer平台包 NodePath.ts 提供了三个 layerlayerPosix固定使用 Node 的node:pathPOSIX 实现layerWin32固定使用 Windows 实现...NodePath.win32toFileUrl调用NodeUrl.pathToFileURL(path, { windows: true })layer默认直接展开宿主平台的node:pathtoFileUrl调用NodeUrl.pathToFileURL(path, { windows: undefined })即由 Node 依据当前宿主平台决定是否按 Windows 规则处理。其中toFileUrl的实现NodePath.ts用Effect.try包裹NodeUrl.pathToFileURL失败时映射为PlatformError.BadArgumentmodule 为Path、method 为toFileUrltoFileUrl: (path: string): Effect.EffectURL, BadArgument Effect.try({ try: () NodeUrl.pathToFileURL(path, { windows }), catch: (cause) new BadArgument({ module: Path, method: toFileUrl, cause }) })因此在 Windows 上调用方应提供NodePath.layer跟随宿主平台或NodePath.layerWin32强制 Windows 语义而不是核心库的Path.layer。例如import { Migrator } from effect/unstable/sql/Migrator import { NodePath } from effect/platform-node-shared/NodePath import { NodeFileSystem } from effect/platform-node/NodeFileSystem import { Layer } from effect const MigrationsLayer Layer.provide( Migrator.make({ loader: Migrator.fromFileSystem(./migrations) }), Layer.merge(NodeFileSystem.layer, NodePath.layer) )如果同时提供了NodeServices.layer这类聚合层它已经包含FileSystem与Path则无需再单独装配。另外若需要Path服务的fromFileUrlURL 转回路径NodePath的各 layer 同样通过NodeUrl.fileURLToPath提供实现对称的往返转换NodePath.ts。迁移加载的完整流程与错误通道fromFileSystem只是Migrator.make的加载器之一。完整迁移流程Migrator.ts如下获取SqlClient按方言创建迁移表默认表名effect_sql_migrationsmssql/mysql/pg/其他方言各有不同的建表 SQL在事务内对 pg 方言先执行LOCK TABLE ... IN ACCESS EXCLUSIVE MODE然后查询最新迁移 id、运行 loader 获取迁移列表检查重复 id发现重复返回MigrationErrorkind 为Duplicates过滤出 id 大于最新已执行迁移的待运行迁移逐个执行loadMigration即这里触发fromFileSystem中构造的import(url.href)动态导入并处理默认导出是否为 Effect、是否缺失默认导出等错误kind 为ImportError将待运行迁移插入迁移表约束冲突会转换为MigrationErrorkind 为Locked表示并发运行被锁定且该错误会被捕获后按“已完成”处理依次执行每个迁移 Effect失败时包装为MigrationErrorkind 为Failed并用annotateLogs(migration_id / migration_name)与 spanMigrator ${id}_${name}记录日志可选配置了schemaDirectory时迁移完成后将 schema 导出到{schemaDirectory}/_schema.sql。测试 Migrator.test.ts 验证了fromFileSystem能正确识别js/ts/mjs/mts四种扩展名并按 id 排序同时忽略cjs等不匹配文件Migrator.test.ts 通过模拟 Windows 风格的Pathlayer验证了最终import的模块说明符是Path服务解析出的 file URL并且测试夹具 0001_first.js 被真实加载导出marker loaded被断言为loaded。这与仓库 CHANGELOGpackages/effect/CHANGELOG.md中对应的 patch 条目相互印证。总结本次修复解决了一个真实且隐蔽的跨平台问题import不接受 Windows 绝对路径字符串作为模块说明符必须使用 file URL。Migrator.fromFileSystem通过在Path服务上调用toFileUrl完成了路径到 URL 的转换并借助orDie将转换失败归入迁移导入错误通道。对使用者而言最需要关注的两点是类型要求从LoaderFileSystem变为LoaderFileSystem | Path单独提供FileSystem的调用方需要补上PathlayerWindows 上务必使用平台感知的Path实现如NodePath.layer或NodePath.layerWin32POSIX 语义的核心Path.layer无法正确处理盘符路径。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表