Skip to content

Commit efecdab

Browse files
committed
fix(typesync): make dynamic import work on Windows
Problem ------- `@effect/sql`’s File-system loader passes a raw Win32 path such as C:\project\apps\typesync\src\migrations\1_create_table__app.js directly to `import()`. On Windows the Node ESM loader interprets the leading “C:” as a URL scheme and throws: Only URLs with a scheme in: file, data, and node are supported … Received protocol 'c:' (Linux and macOS do not throw because the path starts with '/'.) See Node issue: nodejs/node#31710 See Effect issue: Effect-TS/effect#4297 Fix --- 1. Keep `dir` as a *plain OS path* when calling `FileSystem.readDirectory(dir)` (still required by platform-fs). 2. Convert the specifier used by the dynamic `import()` to a **file URL** with `pathToFileURL(...).href`. 3. Extend the filename RegExp to accept both “/” and “\” separators. Result ------ • Behaviour on POSIX remains unchanged. • Migrations now load correctly on Windows. • No other library code is touched; the change can be removed once the upstream patch is released. Affected files: • apps/typesync/src/Database.ts – use fromFileSystem() helper • apps/typesync/src/Utils.ts – add fromFileSystem() helper
1 parent b043cf2 commit efecdab

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

‎apps/typesync/src/Database.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { Chunk, Console, Effect, Array as EffectArray, Layer, Option, Order, Sch
66

77
import * as TypesyncDomain from '../domain/Domain.js';
88
import * as Domain from './Domain.js';
9+
import { fromFileSystem } from './Utils.js';
910

1011
const SqlLive = SqliteClient.layer({
1112
filename: '.typesync.db',
1213
});
14+
1315
const MigratorLive = Migrator.layer({
14-
loader: Migrator.fromFileSystem(fileURLToPath(new URL('migrations', import.meta.url))),
15-
}).pipe(Layer.provide(SqlLive));
16+
loader: fromFileSystem(fileURLToPath(new URL('migrations', import.meta.url))),
17+
}).pipe(Layer.provide(SqlLive))
1618

1719
const DatabaseLive = Layer.mergeAll(SqlLive, MigratorLive).pipe(Layer.provide(NodeContext.layer));
1820

‎apps/typesync/src/Utils.ts‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,54 @@ export class InvalidInputError extends Data.TaggedError('/typesync/errors/Invali
125125
readonly input: string;
126126
readonly cause: unknown;
127127
}> {}
128+
129+
/* ------------------------------------------------------------------ */
130+
/* Windows-safe migration loader */
131+
/* ------------------------------------------------------------------ */
132+
133+
import { pathToFileURL } from "node:url"
134+
import { FileSystem } from "@effect/platform/FileSystem"
135+
import * as Effect from "effect/Effect"
136+
import type { Loader, ResolvedMigration } from "@effect/sql/Migrator"
137+
import { MigrationError } from "@effect/sql/Migrator"
138+
139+
/**
140+
* Patched version of
141+
* `@effect/sql/Migrator/FileSystem.fromFileSystem`.
142+
*
143+
* The only difference is that the dynamic `import()` receives a proper
144+
* `file://` URL, so it works on Windows as well as on Linux / macOS.
145+
*/
146+
export const fromFileSystem = (
147+
dir: string,
148+
): Loader<FileSystem> =>
149+
FileSystem.pipe(
150+
/* read directory ----------------------------------------------------- */
151+
Effect.flatMap((FS) => FS.readDirectory(dir)),
152+
Effect.mapError(
153+
(e) => new MigrationError({ reason: "failed", message: e.message }),
154+
),
155+
/* build migration list ---------------------------------------------- */
156+
Effect.map((files): ReadonlyArray<ResolvedMigration> =>
157+
files
158+
.flatMap((file) => {
159+
const m =
160+
file.match(/^(?:.*[\\/])?(\d+)_([^.]+)\.(js|ts)$/) // win/posix
161+
if (!m) return []
162+
const [basename, id, name] = m
163+
return [
164+
[
165+
Number(id),
166+
name,
167+
Effect.promise(() =>
168+
import(
169+
/* @vite-ignore */ /* webpackIgnore: true */
170+
pathToFileURL(`${dir}/${basename}`).href,
171+
),
172+
),
173+
],
174+
] as const
175+
})
176+
.sort(([a], [b]) => a - b),
177+
),
178+
)

0 commit comments

Comments
 (0)