diff --git a/packages/engine-core/src/library/DefaultLibraryLoader.ts b/packages/engine-core/src/library/DefaultLibraryLoader.ts index adbf91f5fed0..1e3a0c761cbc 100644 --- a/packages/engine-core/src/library/DefaultLibraryLoader.ts +++ b/packages/engine-core/src/library/DefaultLibraryLoader.ts @@ -4,6 +4,7 @@ import type { Platform } from '@prisma/get-platform' import { getNodeAPIName, getPlatform, getPlatformWithOSResult } from '@prisma/get-platform' import chalk from 'chalk' import fs from 'fs' +import os from 'os' import path from 'path' import { EngineConfig } from '../common/Engine' @@ -16,9 +17,56 @@ import { Library, LibraryLoader } from './types/Library' const debug = Debug('prisma:client:libraryEngine:loader') -export function load(id: string): T { - // this require needs to be resolved at runtime, tell webpack to ignore it - return eval('require')(id) as T +const libraryCacheSymbol = Symbol('PrismaLibraryEngineCache') + +type LibraryCache = Record + +type GlobalWithCache = typeof globalThis & { + [libraryCacheSymbol]?: LibraryCache +} + +function getLibraryCache(): LibraryCache { + const globalWithCache = globalThis as GlobalWithCache + if (globalWithCache[libraryCacheSymbol] === undefined) { + globalWithCache[libraryCacheSymbol] = {} + } + return globalWithCache[libraryCacheSymbol] +} + +export function load(libraryPath: string): Library { + const cache = getLibraryCache() + + if (cache[libraryPath] !== undefined) { + return cache[libraryPath]! + } + + // `toNamespacedPath` is required for native addons on Windows, but it's a no-op on other systems. + // We call it here unconditionally just like `.node` CommonJS loader in Node.js does. + const fullLibraryPath = path.toNamespacedPath(libraryPath) + const libraryModule = { exports: {} as Library } + + let flags = 0 + + if (process.platform !== 'win32') { + // Add RTLD_LAZY and RTLD_DEEPBIND on Unix. + // + // RTLD_LAZY: this is what Node.js uses by default on all Unix-like systems + // if no flags were passed to dlopen from JavaScript side. + // + // RTLD_DEEPBIND: this is not a part of POSIX standard but a widely + // supported extension. It prevents issues when we dynamically link to + // system OpenSSL on Linux but the dynamic linker resolves the symbols from + // the Node.js binary instead. + // + // @ts-expect-error TODO: typings don't define dlopen -- needs to be fixed upstream + flags = os.constants.dlopen.RTLD_LAZY | os.constants.dlopen.RTLD_DEEPBIND + } + + // @ts-expect-error TODO: typings don't define dlopen -- needs to be fixed upstream + process.dlopen(libraryModule, fullLibraryPath, flags) + + cache[libraryPath] = libraryModule.exports + return libraryModule.exports } export class DefaultLibraryLoader implements LibraryLoader {