diff --git a/.changeset/new-eels-drop.md b/.changeset/new-eels-drop.md new file mode 100644 index 0000000000000..b25ff06594663 --- /dev/null +++ b/.changeset/new-eels-drop.md @@ -0,0 +1,6 @@ +--- +"@medusajs/framework": patch +"@medusajs/utils": patch +--- + +Fix loaders path diff --git a/packages/core/framework/src/feature-flags/feature-flag-loader.ts b/packages/core/framework/src/feature-flags/feature-flag-loader.ts index 038a890617bdb..aa27dd1eb6a86 100644 --- a/packages/core/framework/src/feature-flags/feature-flag-loader.ts +++ b/packages/core/framework/src/feature-flags/feature-flag-loader.ts @@ -1,3 +1,4 @@ +import { trackFeatureFlag } from "@medusajs/telemetry" import { ContainerRegistrationKeys, dynamicImport, @@ -7,15 +8,14 @@ import { isString, isTruthy, objectFromStringPath, + readDirRecursive, } from "@medusajs/utils" -import { trackFeatureFlag } from "@medusajs/telemetry" +import { asFunction } from "awilix" import { join, normalize } from "path" +import { configManager } from "../config" +import { container } from "../container" import { logger } from "../logger" import { FlagSettings } from "./types" -import { container } from "../container" -import { asFunction } from "awilix" -import { configManager } from "../config" -import { readdir } from "fs/promises" export const featureFlagRouter = new FlagRouter({}) @@ -95,36 +95,34 @@ export async function featureFlagsLoader( const flagDir = normalize(sourcePath) - await readdir(flagDir, { recursive: true, withFileTypes: true }).then( - async (files) => { - if (!files?.length) { - return - } - - files.map(async (file) => { - if (file.isDirectory()) { - return await featureFlagsLoader(join(flagDir, file.name)) - } + await readDirRecursive(flagDir).then(async (files) => { + if (!files?.length) { + return + } - if ( - excludedExtensions.some((ext) => file.name.endsWith(ext)) || - excludedFiles.includes(file.name) - ) { - return - } + files.map(async (file) => { + if (file.isDirectory()) { + return await featureFlagsLoader(join(flagDir, file.name)) + } - const fileExports = await dynamicImport(join(flagDir, file.name)) - const featureFlag = fileExports.default + if ( + excludedExtensions.some((ext) => file.name.endsWith(ext)) || + excludedFiles.includes(file.name) + ) { + return + } - if (!featureFlag) { - return - } + const fileExports = await dynamicImport(join(flagDir, file.name)) + const featureFlag = fileExports.default - registerFlag(featureFlag, projectConfigFlags) + if (!featureFlag) { return - }) - } - ) + } + + registerFlag(featureFlag, projectConfigFlags) + return + }) + }) return featureFlagRouter } diff --git a/packages/core/framework/src/http/router.ts b/packages/core/framework/src/http/router.ts index 5d1634c939a19..90124df53df8b 100644 --- a/packages/core/framework/src/http/router.ts +++ b/packages/core/framework/src/http/router.ts @@ -2,6 +2,7 @@ import { dynamicImport, parseCorsOrigins, promiseAll, + readDirRecursive, resolveExports, wrapHandler, } from "@medusajs/utils" @@ -14,6 +15,7 @@ import { text, urlencoded, } from "express" +import { Dirent } from "fs" import { readdir } from "fs/promises" import { extname, join, parse, sep } from "path" import { configManager } from "../config" @@ -527,11 +529,8 @@ export class ApiRoutesLoader { protected async createRoutesMap(): Promise { await promiseAll( - await readdir(this.#sourceDir, { - recursive: true, - withFileTypes: true, - }).then((entries) => { - const fileEntries = entries.filter((entry) => { + await readDirRecursive(this.#sourceDir).then((entries) => { + const fileEntries = entries.filter((entry: Dirent) => { const fullPathFromSource = join(entry.path, entry.name).replace( this.#sourceDir, "" @@ -549,7 +548,7 @@ export class ApiRoutesLoader { ) }) - return fileEntries.map(async (entry) => { + return fileEntries.map(async (entry: Dirent) => { const path = join(entry.path, entry.name) return this.createRoutesDescriptor(path) }) diff --git a/packages/core/framework/src/jobs/job-loader.ts b/packages/core/framework/src/jobs/job-loader.ts index 52a85766ba337..a2059981cd6be 100644 --- a/packages/core/framework/src/jobs/job-loader.ts +++ b/packages/core/framework/src/jobs/job-loader.ts @@ -5,13 +5,15 @@ import { isObject, MedusaError, promiseAll, + readDirRecursive, } from "@medusajs/utils" import { createStep, createWorkflow, StepResponse, } from "@medusajs/workflows-sdk" -import { access, readdir } from "fs/promises" +import { Dirent } from "fs" +import { access } from "fs/promises" import { join } from "path" import { logger } from "../logger" @@ -138,11 +140,8 @@ export class JobLoader { return } - return await readdir(sourcePath, { - recursive: true, - withFileTypes: true, - }).then(async (entries) => { - const fileEntries = entries.filter((entry) => { + return await readDirRecursive(sourcePath).then(async (entries) => { + const fileEntries = entries.filter((entry: Dirent) => { return ( !entry.isDirectory() && !this.#excludes.some((exclude) => exclude.test(entry.name)) @@ -152,7 +151,7 @@ export class JobLoader { logger.debug(`Registering jobs from ${sourcePath}.`) return await promiseAll( - fileEntries.map(async (entry) => { + fileEntries.map(async (entry: Dirent) => { const fullPath = join(entry.path, entry.name) const module_ = await dynamicImport(fullPath) diff --git a/packages/core/framework/src/links/link-loader.ts b/packages/core/framework/src/links/link-loader.ts index 85456be8215be..1f46f7587b0f8 100644 --- a/packages/core/framework/src/links/link-loader.ts +++ b/packages/core/framework/src/links/link-loader.ts @@ -1,7 +1,8 @@ -import { dynamicImport, promiseAll } from "@medusajs/utils" -import { logger } from "../logger" -import { access, readdir } from "fs/promises" +import { dynamicImport, promiseAll, readDirRecursive } from "@medusajs/utils" +import { Dirent } from "fs" +import { access } from "fs/promises" import { join } from "path" +import { logger } from "../logger" export class LinkLoader { /** @@ -43,11 +44,8 @@ export class LinkLoader { return } - return await readdir(sourcePath, { - recursive: true, - withFileTypes: true, - }).then(async (entries) => { - const fileEntries = entries.filter((entry) => { + return await readDirRecursive(sourcePath).then(async (entries) => { + const fileEntries = entries.filter((entry: Dirent) => { return ( !entry.isDirectory() && !this.#excludes.some((exclude) => exclude.test(entry.name)) @@ -57,7 +55,7 @@ export class LinkLoader { logger.debug(`Registering links from ${sourcePath}.`) return await promiseAll( - fileEntries.map(async (entry) => { + fileEntries.map(async (entry: Dirent) => { const fullPath = join(entry.path, entry.name) return await dynamicImport(fullPath) }) diff --git a/packages/core/framework/src/subscribers/subscriber-loader.ts b/packages/core/framework/src/subscribers/subscriber-loader.ts index 0153d6c8426f9..a029eaae0afcc 100644 --- a/packages/core/framework/src/subscribers/subscriber-loader.ts +++ b/packages/core/framework/src/subscribers/subscriber-loader.ts @@ -4,11 +4,13 @@ import { kebabCase, Modules, promiseAll, + readDirRecursive, resolveExports, } from "@medusajs/utils" -import { access, readdir } from "fs/promises" +import { access } from "fs/promises" import { join, parse } from "path" +import { Dirent } from "fs" import { configManager } from "../config" import { container } from "../container" import { logger } from "../logger" @@ -137,10 +139,7 @@ export class SubscriberLoader { } private async createMap(dirPath: string) { - const promises = await readdir(dirPath, { - recursive: true, - withFileTypes: true, - }).then(async (entries) => { + const promises = await readDirRecursive(dirPath).then(async (entries) => { const fileEntries = entries.filter((entry) => { return ( !entry.isDirectory() && @@ -150,7 +149,7 @@ export class SubscriberLoader { logger.debug(`Registering subscribers from ${dirPath}.`) - return fileEntries.flatMap(async (entry) => { + return fileEntries.flatMap(async (entry: Dirent) => { const fullPath = join(entry.path, entry.name) return await this.createDescriptor(fullPath) }) diff --git a/packages/core/framework/src/workflows/workflow-loader.ts b/packages/core/framework/src/workflows/workflow-loader.ts index 15fa2886aa299..a8b59453274eb 100644 --- a/packages/core/framework/src/workflows/workflow-loader.ts +++ b/packages/core/framework/src/workflows/workflow-loader.ts @@ -1,7 +1,8 @@ -import { dynamicImport, promiseAll } from "@medusajs/utils" -import { logger } from "../logger" -import { access, readdir } from "fs/promises" +import { dynamicImport, promiseAll, readDirRecursive } from "@medusajs/utils" +import { Dirent } from "fs" +import { access } from "fs/promises" import { join } from "path" +import { logger } from "../logger" export class WorkflowLoader { /** @@ -43,11 +44,8 @@ export class WorkflowLoader { return } - return await readdir(sourcePath, { - recursive: true, - withFileTypes: true, - }).then(async (entries) => { - const fileEntries = entries.filter((entry) => { + return await readDirRecursive(sourcePath).then(async (entries) => { + const fileEntries = entries.filter((entry: Dirent) => { return ( !entry.isDirectory() && !this.#excludes.some((exclude) => exclude.test(entry.name)) @@ -57,7 +55,7 @@ export class WorkflowLoader { logger.debug(`Registering workflows from ${sourcePath}.`) return await promiseAll( - fileEntries.map(async (entry) => { + fileEntries.map(async (entry: Dirent) => { const fullPath = join(entry.path, entry.name) return await dynamicImport(fullPath) }) diff --git a/packages/core/utils/src/common/__tests__/read-dir-recursive.spec.ts b/packages/core/utils/src/common/__tests__/read-dir-recursive.spec.ts new file mode 100644 index 0000000000000..217a6bae33a56 --- /dev/null +++ b/packages/core/utils/src/common/__tests__/read-dir-recursive.spec.ts @@ -0,0 +1,61 @@ +import { readdir } from "fs/promises" +import { join } from "path" +import { readDirRecursive } from "../read-dir-recursive" + +jest.mock("fs/promises") +jest.mock("path") + +describe("readDirRecursive", () => { + it("should recursively read directories and return all entries", async () => { + const mockReaddir = readdir as jest.MockedFunction + const mockJoin = join as jest.MockedFunction + + // dir structure + const dirStructure = { + "/root": [ + { name: "file1.txt", isDirectory: () => false }, + { name: "subdir", isDirectory: () => true }, + ], + "/root/subdir": [ + { name: "file2.txt", isDirectory: () => false }, + { name: "nested", isDirectory: () => true }, + ], + "/root/subdir/nested": [{ name: "file3.txt", isDirectory: () => false }], + } + + mockReaddir.mockImplementation((dir) => { + return dirStructure[dir as string] ?? [] + }) + mockJoin.mockImplementation((...paths) => paths.join("/")) + + const result = await readDirRecursive("/root") + + expect(result).toEqual([ + { name: "file1.txt", isDirectory: expect.any(Function), path: "/root" }, + { name: "subdir", isDirectory: expect.any(Function), path: "/root" }, + { + name: "file2.txt", + isDirectory: expect.any(Function), + path: "/root/subdir", + }, + { + name: "nested", + isDirectory: expect.any(Function), + path: "/root/subdir", + }, + { + name: "file3.txt", + isDirectory: expect.any(Function), + path: "/root/subdir/nested", + }, + ]) + + expect(mockReaddir).toHaveBeenCalledWith("/root", { withFileTypes: true }) + expect(mockReaddir).toHaveBeenCalledWith("/root/subdir", { + withFileTypes: true, + }) + expect(mockReaddir).toHaveBeenCalledWith("/root/subdir/nested", { + withFileTypes: true, + }) + }) +}) diff --git a/packages/core/utils/src/common/file-system.ts b/packages/core/utils/src/common/file-system.ts index 48742be6aaec4..7eba6db170e19 100644 --- a/packages/core/utils/src/common/file-system.ts +++ b/packages/core/utils/src/common/file-system.ts @@ -1,4 +1,3 @@ -import { dirname, join } from "path" import { constants, type Dirent, @@ -8,8 +7,10 @@ import { type StatOptions, type WriteFileOptions, } from "fs" +import { dirname, join } from "path" +import { readDirRecursive } from "./read-dir-recursive" -const { rm, stat, mkdir, access, readdir, readFile, writeFile } = promises +const { rm, stat, mkdir, access, readFile, writeFile } = promises export type JSONFileOptions = WriteFileOptions & { spaces?: number | string @@ -127,10 +128,7 @@ export class FileSystem { */ readDir(dirPath?: string): Promise { const location = dirPath ? this.makePath(dirPath) : this.basePath - return readdir(location, { - recursive: true, - withFileTypes: true, - }) + return readDirRecursive(location) } /** diff --git a/packages/core/utils/src/common/index.ts b/packages/core/utils/src/common/index.ts index 0677e5d653140..b690a56010d3d 100644 --- a/packages/core/utils/src/common/index.ts +++ b/packages/core/utils/src/common/index.ts @@ -56,6 +56,7 @@ export * from "./pick-value-from-object" export * from "./plurailze" export * from "./prefix-array-items" export * from "./promise-all" +export * from "./read-dir-recursive" export * from "./remote-query-object-from-string" export * from "./remote-query-object-to-string" export * from "./remove-nullisih" diff --git a/packages/core/utils/src/common/read-dir-recursive.ts b/packages/core/utils/src/common/read-dir-recursive.ts new file mode 100644 index 0000000000000..77627718417d0 --- /dev/null +++ b/packages/core/utils/src/common/read-dir-recursive.ts @@ -0,0 +1,23 @@ +import { Dirent } from "fs" +import { readdir } from "fs/promises" +import { join } from "path" + +export async function readDirRecursive(dir: string): Promise { + let allEntries: Dirent[] = [] + const readRecursive = async (dir) => { + const entries = await readdir(dir, { withFileTypes: true }) + + for (const entry of entries) { + const fullPath = join(dir, entry.name) + entry.path = dir + allEntries.push(entry) + + if (entry.isDirectory()) { + await readRecursive(fullPath) + } + } + } + + await readRecursive(dir) + return allEntries +}