UNPKG

@tsdiapi/server

Version:

A fully ESM-based, modular TypeScript server built on Fastify

122 lines (107 loc) 3.93 kB
// file-loader.ts import { pathToFileURL, fileURLToPath } from "url"; import { dirname } from "path"; import * as path from "path"; import { Container } from "typedi"; import { readFile } from "fs/promises"; import { find, pathExists } from "fsesm"; import { AppContext } from "./types.js"; /** * In ESM, there is no __filename and __dirname, so we calculate them via import.meta.url */ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** * Returns the absolute path to the root folder (one level above the current file) * or to a file/folder if relativePath is specified. * * @param {string} [relativePath=""] - Relative path from the root directory * @returns {string} Absolute path */ export function getAppPath(relativePath: string = ""): string { const rootDir = path.resolve(__dirname, "../"); return relativePath ? path.join(rootDir, relativePath) : rootDir; } /** * Dynamically imports a file if it exists, * otherwise returns an empty object. * * @param {string} filePath Absolute path to the file * @returns {Promise<any>} Module or {} */ export async function fileImport(filePath: string): Promise<any> { try { if (!await pathExists(filePath)) { console.warn(`File not found: ${filePath}`); return {}; } // Convert path to file:// URL – ESM import requires URL const fileUrl = pathToFileURL(filePath).href; const importedModule = await import(fileUrl); return importedModule.default || importedModule; } catch (error) { console.error(`Error importing file: ${filePath}`, error); return {}; } } /** * Loads a module from a relative path (relative to the root). * * @param {string} relativePath Relative path * @returns {Promise<any>} Imported module */ export async function loadProjectFile(relativePath: string): Promise<any> { const fullPath = getAppPath(relativePath); return fileImport(fullPath); } /** * Loads a config file from /config/<relativePath> * * @param {string} relativePath File name in the /config folder * @returns {Promise<any>} */ export async function loadProjectConfigFile(relativePath: string): Promise<any> { const fullPath = getAppPath("/config/" + relativePath); return fileImport(fullPath); } /** * Reads a file as text * * @param {string} relativePath Relative path to the file * @returns {Promise<string>} File content */ export async function fileLoadAsText(relativePath: string): Promise<string> { const fullPath = getAppPath(relativePath); return readFile(fullPath, "utf-8"); } export async function fileLoaderWithContext(pattern: string, context: AppContext, cwd = __dirname) { const matchedFiles = await find(pattern, { cwd, absolute: true }); for (const file of matchedFiles) { const fileUrl = pathToFileURL(file).href; const imported = await import(fileUrl); if (imported.default && typeof imported.default === 'function') { await imported.default(context); } } } export async function fileLoader(pattern: string, cwd: string, setToContainer: boolean = false): Promise<any[]> { // Fix slashes to work on Windows / POSIX // Find files by pattern const matchedFiles = await find(pattern, { cwd, absolute: true }); const modules: any[] = []; for (const file of matchedFiles) { const imported = await fileImport(file); if (imported) { modules.push(imported); } // If DI is needed – register default export in the container if (setToContainer && imported instanceof Object) { Container.get(imported); } } return modules; } /** * Default export, if needed */ export default fileLoader;