fix(framework): handle deprecated Dirent path (#10179)

What:

- `Dirent` class from `NodeJS` has different properties in different versions, causing issues in our loaders.
- Util `readDirRecursive` was introduced, avoiding old node versions to break

FIXES: https://github.com/medusajs/medusa/issues/8419
This commit is contained in:
Carlos R. L. Rodrigues
2024-11-20 17:25:48 -03:00
committed by GitHub
parent ec1ab4db87
commit 42c08fa8e0
11 changed files with 153 additions and 73 deletions

View File

@@ -0,0 +1,6 @@
---
"@medusajs/framework": patch
"@medusajs/utils": patch
---
Fix loaders path

View File

@@ -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) {
await readDirRecursive(flagDir).then(async (files) => {
if (!files?.length) {
return
}
files.map(async (file) => {
if (file.isDirectory()) {
return await featureFlagsLoader(join(flagDir, file.name))
}
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
}
const fileExports = await dynamicImport(join(flagDir, file.name))
const featureFlag = fileExports.default
if (!featureFlag) {
return
}
registerFlag(featureFlag, projectConfigFlags)
if (!featureFlag) {
return
})
}
)
}
registerFlag(featureFlag, projectConfigFlags)
return
})
})
return featureFlagRouter
}

View File

@@ -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<void> {
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)
})

View File

@@ -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)

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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)
})

View File

@@ -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<typeof readdir>
const mockJoin = join as jest.MockedFunction<typeof join>
// 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,
})
})
})

View File

@@ -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<Dirent[]> {
const location = dirPath ? this.makePath(dirPath) : this.basePath
return readdir(location, {
recursive: true,
withFileTypes: true,
})
return readDirRecursive(location)
}
/**

View File

@@ -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"

View File

@@ -0,0 +1,23 @@
import { Dirent } from "fs"
import { readdir } from "fs/promises"
import { join } from "path"
export async function readDirRecursive(dir: string): Promise<Dirent[]> {
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
}