feat: remove dead code and refactor the logic of resolving plugins (#10874)
This commit is contained in:
@@ -26,7 +26,7 @@ const main = async function ({ directory, modules }) {
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const plugins = getResolvedPlugins(directory, configModule, true) || []
|
||||
const plugins = await getResolvedPlugins(directory, configModule, true)
|
||||
const linksSourcePaths = plugins.map((plugin) =>
|
||||
join(plugin.resolve, "links")
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function migrate({
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const plugins = getResolvedPlugins(directory, configModule, true) || []
|
||||
const plugins = await getResolvedPlugins(directory, configModule, true)
|
||||
const linksSourcePaths = plugins.map((plugin) =>
|
||||
join(plugin.resolve, "links")
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ const main = async function ({ directory, modules }) {
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const plugins = getResolvedPlugins(directory, configModule, true) || []
|
||||
const plugins = await getResolvedPlugins(directory, configModule, true)
|
||||
const linksSourcePaths = plugins.map((plugin) =>
|
||||
join(plugin.resolve, "links")
|
||||
)
|
||||
|
||||
@@ -187,7 +187,7 @@ const main = async function ({ directory, executeSafe, executeAll }) {
|
||||
|
||||
const medusaAppLoader = new MedusaAppLoader()
|
||||
|
||||
const plugins = getResolvedPlugins(directory, configModule, true) || []
|
||||
const plugins = await getResolvedPlugins(directory, configModule, true)
|
||||
const linksSourcePaths = plugins.map((plugin) =>
|
||||
join(plugin.resolve, "links")
|
||||
)
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
import path from "path"
|
||||
import { defineConfig, FileSystem } from "@medusajs/framework/utils"
|
||||
import { getResolvedPlugins } from "../helpers/resolve-plugins"
|
||||
|
||||
const BASE_DIR = path.join(__dirname, "sample-proj")
|
||||
const fs = new FileSystem(BASE_DIR)
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.cleanup()
|
||||
})
|
||||
|
||||
describe("getResolvedPlugins | relative paths", () => {
|
||||
test("resolve configured plugins", async () => {
|
||||
await fs.createJson("plugins/dummy/package.json", {
|
||||
name: "my-dummy-plugin",
|
||||
version: "1.0.0",
|
||||
})
|
||||
|
||||
const plugins = await getResolvedPlugins(
|
||||
fs.basePath,
|
||||
defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
resolve: "./plugins/dummy",
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
false
|
||||
)
|
||||
|
||||
expect(plugins).toEqual([
|
||||
{
|
||||
resolve: path.join(fs.basePath, "./plugins/dummy/build"),
|
||||
name: "my-dummy-plugin",
|
||||
id: "my-dummy-plugin",
|
||||
options: { apiKey: "asecret" },
|
||||
version: "1.0.0",
|
||||
modules: [],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("scan plugin modules", async () => {
|
||||
await fs.createJson("plugins/dummy/package.json", {
|
||||
name: "my-dummy-plugin",
|
||||
version: "1.0.0",
|
||||
})
|
||||
await fs.create("plugins/dummy/build/modules/blog/index.js", ``)
|
||||
|
||||
const plugins = await getResolvedPlugins(
|
||||
fs.basePath,
|
||||
defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
resolve: "./plugins/dummy",
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
false
|
||||
)
|
||||
|
||||
expect(plugins).toEqual([
|
||||
{
|
||||
resolve: path.join(fs.basePath, "./plugins/dummy/build"),
|
||||
name: "my-dummy-plugin",
|
||||
id: "my-dummy-plugin",
|
||||
options: { apiKey: "asecret" },
|
||||
version: "1.0.0",
|
||||
modules: [
|
||||
{
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
resolve: "./plugins/dummy/build/modules/blog",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("throw error when package.json file is missing", async () => {
|
||||
const resolvePlugins = async () =>
|
||||
getResolvedPlugins(
|
||||
fs.basePath,
|
||||
defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
resolve: "./plugins/dummy",
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
false
|
||||
)
|
||||
|
||||
await expect(resolvePlugins()).rejects.toThrow(
|
||||
`Unable to resolve plugin "./plugins/dummy". Make sure the plugin directory has a package.json file`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getResolvedPlugins | package reference", () => {
|
||||
test("resolve configured plugins", async () => {
|
||||
await fs.createJson("package.json", {})
|
||||
await fs.createJson("node_modules/@plugins/dummy/package.json", {
|
||||
name: "my-dummy-plugin",
|
||||
version: "1.0.0",
|
||||
})
|
||||
|
||||
const plugins = await getResolvedPlugins(
|
||||
fs.basePath,
|
||||
defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
resolve: "@plugins/dummy",
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
false
|
||||
)
|
||||
|
||||
expect(plugins).toEqual([
|
||||
{
|
||||
resolve: path.join(fs.basePath, "node_modules/@plugins/dummy/build"),
|
||||
name: "my-dummy-plugin",
|
||||
id: "my-dummy-plugin",
|
||||
options: { apiKey: "asecret" },
|
||||
version: "1.0.0",
|
||||
modules: [],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("scan plugin modules", async () => {
|
||||
await fs.createJson("package.json", {})
|
||||
await fs.createJson("node_modules/@plugins/dummy/package.json", {
|
||||
name: "my-dummy-plugin",
|
||||
version: "1.0.0",
|
||||
})
|
||||
await fs.create(
|
||||
"node_modules/@plugins/dummy/build/modules/blog/index.js",
|
||||
``
|
||||
)
|
||||
|
||||
const plugins = await getResolvedPlugins(
|
||||
fs.basePath,
|
||||
defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
resolve: "@plugins/dummy",
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
false
|
||||
)
|
||||
|
||||
expect(plugins).toEqual([
|
||||
{
|
||||
resolve: path.join(fs.basePath, "node_modules/@plugins/dummy/build"),
|
||||
name: "my-dummy-plugin",
|
||||
id: "my-dummy-plugin",
|
||||
options: { apiKey: "asecret" },
|
||||
version: "1.0.0",
|
||||
modules: [
|
||||
{
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
resolve: "@plugins/dummy/build/modules/blog",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("throw error when package.json file is missing", async () => {
|
||||
const resolvePlugins = async () =>
|
||||
getResolvedPlugins(
|
||||
fs.basePath,
|
||||
defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
resolve: "@plugins/dummy",
|
||||
options: {
|
||||
apiKey: "asecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
false
|
||||
)
|
||||
|
||||
await expect(resolvePlugins()).rejects.toThrow(
|
||||
`Unable to resolve plugin "@plugins/dummy". Make sure the plugin directory has a package.json file`
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,148 +1,103 @@
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
import { isString, readDir } from "@medusajs/framework/utils"
|
||||
import { ConfigModule, PluginDetails } from "@medusajs/framework/types"
|
||||
import { isString } from "@medusajs/framework/utils"
|
||||
import fs from "fs"
|
||||
import { sync as existsSync } from "fs-exists-cached"
|
||||
import path, { isAbsolute } from "path"
|
||||
|
||||
const MEDUSA_APP_SOURCE_PATH = "src"
|
||||
export const MEDUSA_PROJECT_NAME = "project-plugin"
|
||||
|
||||
function createPluginId(name: string): string {
|
||||
return name
|
||||
}
|
||||
|
||||
function createFileContentHash(path, files): string {
|
||||
function createFileContentHash(path: string, files: string): string {
|
||||
return path + files
|
||||
}
|
||||
|
||||
function getExtensionDirectoryPath() {
|
||||
return "src"
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugin details from a path. Return undefined if does not contains a package.json
|
||||
* @param pluginName
|
||||
* @param path
|
||||
* @param includeExtensionDirectoryPath should include src | dist for the resolved details
|
||||
* Returns the absolute path to the package.json file for a
|
||||
* given plugin identifier.
|
||||
*/
|
||||
function loadPluginDetails({
|
||||
pluginName,
|
||||
resolvedPath,
|
||||
includeExtensionDirectoryPath,
|
||||
}: {
|
||||
pluginName: string
|
||||
resolvedPath: string
|
||||
includeExtensionDirectoryPath?: boolean
|
||||
}) {
|
||||
if (existsSync(`${resolvedPath}/package.json`)) {
|
||||
const packageJSON = JSON.parse(
|
||||
fs.readFileSync(`${resolvedPath}/package.json`, `utf-8`)
|
||||
async function resolvePluginPkgFile(
|
||||
rootDirectory: string,
|
||||
pluginPath: string
|
||||
): Promise<{ path: string; contents: any }> {
|
||||
try {
|
||||
const pkgJSONPath = require.resolve(path.join(pluginPath, "package.json"), {
|
||||
paths: [rootDirectory],
|
||||
})
|
||||
const packageJSONContents = JSON.parse(
|
||||
await fs.readFile(pkgJSONPath, "utf-8")
|
||||
)
|
||||
const name = packageJSON.name || pluginName
|
||||
|
||||
const extensionDirectoryPath = getExtensionDirectoryPath()
|
||||
const resolve = includeExtensionDirectoryPath
|
||||
? path.join(resolvedPath, extensionDirectoryPath)
|
||||
: resolvedPath
|
||||
|
||||
return {
|
||||
resolve,
|
||||
name,
|
||||
id: createPluginId(name),
|
||||
options: {},
|
||||
version: packageJSON.version || createFileContentHash(path, `**`),
|
||||
return { path: pkgJSONPath, contents: packageJSONContents }
|
||||
} catch (error) {
|
||||
if (error.code === "MODULE_NOT_FOUND" || error.code === "ENOENT") {
|
||||
throw new Error(
|
||||
`Unable to resolve plugin "${pluginPath}". Make sure the plugin directory has a package.json file`
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
// Make package.json a requirement for local plugins too
|
||||
throw new Error(`Plugin ${pluginName} requires a package.json file`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the correct path for the plugin. If it is a local plugin it will be
|
||||
* found in the plugins folder. Otherwise we will look for the plugin in the
|
||||
* installed npm packages.
|
||||
* @param {string} pluginName - the name of the plugin to find. Should match
|
||||
* @param {string} pluginPath - the name of the plugin to find. Should match
|
||||
* the name of the folder where the plugin is contained.
|
||||
* @return {object} the plugin details
|
||||
*/
|
||||
function resolvePlugin(pluginName: string): {
|
||||
resolve: string
|
||||
id: string
|
||||
name: string
|
||||
options: Record<string, unknown>
|
||||
version: string
|
||||
} {
|
||||
if (!isAbsolute(pluginName)) {
|
||||
let resolvedPath = path.resolve(`./plugins/${pluginName}`)
|
||||
const doesExistsInPlugin = existsSync(resolvedPath)
|
||||
async function resolvePlugin(
|
||||
rootDirectory: string,
|
||||
pluginPath: string,
|
||||
options?: any
|
||||
): Promise<PluginDetails> {
|
||||
const pkgJSON = await resolvePluginPkgFile(rootDirectory, pluginPath)
|
||||
const resolvedPath = path.dirname(pkgJSON.path)
|
||||
|
||||
if (doesExistsInPlugin) {
|
||||
return loadPluginDetails({
|
||||
pluginName,
|
||||
resolvedPath,
|
||||
})
|
||||
}
|
||||
const name = pkgJSON.contents.name || pluginPath
|
||||
const srcDir = pkgJSON.contents.main
|
||||
? path.dirname(pkgJSON.contents.main)
|
||||
: "build"
|
||||
|
||||
// Find the plugin in the file system
|
||||
resolvedPath = path.resolve(pluginName)
|
||||
const doesExistsInFileSystem = existsSync(resolvedPath)
|
||||
const resolve = path.join(resolvedPath, srcDir)
|
||||
const modules = await readDir(path.join(resolve, "modules"), {
|
||||
ignoreMissing: true,
|
||||
})
|
||||
const pluginOptions = options ?? {}
|
||||
|
||||
if (doesExistsInFileSystem) {
|
||||
return loadPluginDetails({
|
||||
pluginName,
|
||||
resolvedPath,
|
||||
includeExtensionDirectoryPath: true,
|
||||
})
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find the plugin "${pluginName}".`)
|
||||
}
|
||||
|
||||
try {
|
||||
// If the path is absolute, resolve the directory of the internal plugin,
|
||||
// otherwise resolve the directory containing the package.json
|
||||
const resolvedPath = require.resolve(pluginName, {
|
||||
paths: [process.cwd()],
|
||||
})
|
||||
|
||||
const packageJSON = JSON.parse(
|
||||
fs.readFileSync(`${resolvedPath}/package.json`, `utf-8`)
|
||||
)
|
||||
|
||||
const computedResolvedPath = path.join(resolvedPath, "dist")
|
||||
|
||||
return {
|
||||
resolve: computedResolvedPath,
|
||||
id: createPluginId(packageJSON.name),
|
||||
name: packageJSON.name,
|
||||
options: {},
|
||||
version: packageJSON.version,
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`Unable to find plugin "${pluginName}". Perhaps you need to install its package?`
|
||||
)
|
||||
return {
|
||||
resolve,
|
||||
name,
|
||||
id: createPluginId(name),
|
||||
options: pluginOptions,
|
||||
version: pkgJSON.contents.version || "0.0.0",
|
||||
modules: modules.map((mod) => {
|
||||
return {
|
||||
resolve: `${pluginPath}/${srcDir}/modules/${mod.name}`,
|
||||
options: pluginOptions,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
export function getResolvedPlugins(
|
||||
export async function getResolvedPlugins(
|
||||
rootDirectory: string,
|
||||
configModule: ConfigModule,
|
||||
isMedusaProject = false
|
||||
): undefined | PluginDetails[] {
|
||||
const resolved = configModule?.plugins?.map((plugin) => {
|
||||
if (isString(plugin)) {
|
||||
return resolvePlugin(plugin)
|
||||
}
|
||||
|
||||
const details = resolvePlugin(plugin.resolve)
|
||||
details.options = plugin.options
|
||||
|
||||
return details
|
||||
})
|
||||
): Promise<PluginDetails[]> {
|
||||
const resolved = await Promise.all(
|
||||
(configModule?.plugins || []).map(async (plugin) => {
|
||||
if (isString(plugin)) {
|
||||
return resolvePlugin(rootDirectory, plugin)
|
||||
}
|
||||
return resolvePlugin(rootDirectory, plugin.resolve, plugin.options)
|
||||
})
|
||||
)
|
||||
|
||||
if (isMedusaProject) {
|
||||
const extensionDirectoryPath = getExtensionDirectoryPath()
|
||||
const extensionDirectory = path.join(rootDirectory, extensionDirectoryPath)
|
||||
const extensionDirectory = path.join(rootDirectory, MEDUSA_APP_SOURCE_PATH)
|
||||
resolved.push({
|
||||
resolve: extensionDirectory,
|
||||
name: MEDUSA_PROJECT_NAME,
|
||||
|
||||
@@ -146,7 +146,7 @@ export default async ({
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const plugins = getResolvedPlugins(rootDirectory, configModule, true) || []
|
||||
const plugins = await getResolvedPlugins(rootDirectory, configModule, true)
|
||||
const linksSourcePaths = plugins.map((plugin) =>
|
||||
join(plugin.resolve, "links")
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user