docs: added plugins documentation (#10989)

(Should be merged after the next release)

Closes DX-1294
This commit is contained in:
Shahed Nasser
2025-01-27 09:32:53 +00:00
committed by GitHub
parent 0c5f21e893
commit c26ef84cae
26 changed files with 2901 additions and 52 deletions
@@ -0,0 +1,17 @@
export const metadata = {
title: `${pageNumber} Re-Use Customizations with Plugins`,
}
# {metadata.title}
In the previous chapters, you've learned important concepts related to creating modules, implementing commerce features in workflows, exposing those features in API routes, customizing the Medusa Admin dashboard with Admin Extensions, and integrating third-party systems.
You've implemented the brands example within a single Medusa application. However, this approach is not scalable when you want to reuse your customizations across multiple projects.
To reuse your customizations across multiple Medusa applications, such as implementing brands in different projects, you can create a plugin. A plugin is an NPM package that encapsulates your customizations and can be installed in any Medusa application. Plugins can include modules, workflows, API routes, Admin Extensions, and more.
![Diagram showcasing how the Brand Plugin would add its resources to any application it's installed in](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540091/Medusa%20Book/brand-plugin_bk9zi9.jpg)
Medusa provides the tooling to create a plugin package, test it in a local Medusa application, and publish it to NPM.
To learn more about plugins and how to create them, refer to [this chapter](../../fundamentals/plugins/page.mdx).
@@ -198,7 +198,7 @@ For example, assuming you have the [Order, Product, and OrderProduct models from
class HelloModuleService extends MedusaService({
Order,
Product,
OrderProduct
OrderProduct,
}) {}
```
@@ -212,16 +212,16 @@ const orderProduct = await helloModuleService.createOrderProducts({
order_id: "123",
product_id: "123",
metadata: {
test: true
}
test: true,
},
})
// update order-product association
const orderProduct = await helloModuleService.updateOrderProducts({
id: "123",
metadata: {
test: false
}
test: false,
},
})
// delete order-product association
@@ -217,29 +217,29 @@ export const manyToManyColumnHighlights = [
]
```ts highlights={manyToManyColumnHighlights}
import { model } from "@medusajs/framework/utils";
import { model } from "@medusajs/framework/utils"
export const Order = model.define("order_test", {
id: model.id().primaryKey(),
products: model.manyToMany(() => Product, {
pivotEntity: () => OrderProduct
})
pivotEntity: () => OrderProduct,
}),
})
export const Product = model.define("product_test", {
id: model.id().primaryKey(),
orders: model.manyToMany(() => Order)
orders: model.manyToMany(() => Order),
})
export const OrderProduct = model.define("orders_products", {
id: model.id().primaryKey(),
order: model.belongsTo(() => Order, {
mappedBy: "products"
mappedBy: "products",
}),
product: model.belongsTo(() => Product, {
mappedBy: "orders"
mappedBy: "orders",
}),
metadata: model.json().nullable()
metadata: model.json().nullable(),
})
```
@@ -8,9 +8,7 @@ In this chapter, youll learn about passing options to your module from the Me
## What are Module Options?
A module can receive options to customize or configure its functionality.
For example, if youre creating a module that integrates a third-party service, youll want to receive the integration credentials in the options rather than adding them directly in your code.
A module can receive options to customize or configure its functionality. For example, if youre creating a module that integrates a third-party service, youll want to receive the integration credentials in the options rather than adding them directly in your code.
---
@@ -20,7 +18,7 @@ To pass options to a module, add an `options` property to the modules configu
For example:
```js title="medusa-config.ts"
```ts title="medusa-config.ts"
module.exports = defineConfig({
// ...
modules: [
@@ -36,6 +34,28 @@ module.exports = defineConfig({
The `options` propertys value is an object. You can pass any properties you want.
### Pass Options to a Module in a Plugin
If your module is part of a plugin, you can pass options to the module in the plugins configuration.
For example:
```ts title="medusa-config.ts"
import { defineConfig } from "@medusajs/framework/utils"
module.exports = defineConfig({
plugins: [
{
resolve: "@myorg/plugin-name",
options: {
capitalize: true,
},
},
],
})
```
The `options` property in the plugin configuration is passed to all modules in a plugin.
---
## Access Module Options in Main Service
@@ -16,6 +16,18 @@ Medusa removes this overhead by allowing you to easily write custom modules that
As you learn more about Medusa, you will see that modules are central to customizations and integrations. With modules, your Medusa application can turn into a middleware solution for your commerce ecosystem.
<Note title="Use a module if" type="success">
- You want to build a custom feature related to a single domain or integrate a third-party service.
</Note>
<Note title="Don't use a module if" type="error">
- You want to create a reusable package of customizations that include not only modules, but also API routes, workflows, and other customizations. Instead, use a [plugin](../plugins/page.mdx).
</Note>
---
## How to Create a Module?
@@ -136,6 +148,12 @@ You export `BLOG_MODULE` to reference the module's name more reliably when resol
### 4. Add Module to Medusa's Configurations
<Note>
If you're creating the module in a plugin, this step isn't required as the module is registered when the plugin is registered. Learn more about plugins in [this documentation](../plugins/page.mdx).
</Note>
Once you finish building the module, add it to Medusa's configurations to start using it. Medusa will then register the module's main service in the Medusa container, allowing you to resolve and use it in other customizations.
In `medusa-config.ts`, add a `modules` property and pass an array with your custom module:
@@ -165,6 +183,12 @@ You don't have to write migrations yourself. Medusa's CLI tool has a command tha
To generate a migration for the Blog Module, run the following command in your Medusa application's directory:
<Note>
If you're creating the module in a plugin, use the [plugin\:db\:generate command](!resources!/medusa-cli/commands/plugin#plugindbgenerate) instead.
</Note>
```bash
npx medusa db:generate blog
```
@@ -193,6 +217,12 @@ In the migration class, the `up` method creates the table `post` and defines its
To reflect the changes in the generated migration file on the database, run the `db:migrate` command:
<Note>
If you're creating the module in a plugin, run this command on the Medusa application that the plugin is installed in.
</Note>
```bash
npx medusa db:migrate
```
@@ -0,0 +1,304 @@
import { Prerequisites, CardList } from "docs-ui"
export const metadata = {
title: `${pageNumber} Create a Plugin`,
}
# {metadata.title}
In this chapter, you'll learn how to create a Medusa plugin and publish it.
A [plugin](../page.mdx) is a package of reusable Medusa customizations that you can install in any Medusa application. By creating and publishing a plugin, you can reuse your Medusa customizations across multiple projects or share them with the community.
<Note>
Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
</Note>
## 1. Create a Plugin Project
Plugins are created in a separate Medusa project. This makes the development and publishing of the plugin easier. Later, you'll install that plugin in your Medusa application to test it out and use it.
Medusa's `create-medusa-app` CLI tool provides the option to create a plugin project. Run the following command to create a new plugin project:
```bash
npx create-medusa-app my-plugin --plugin
```
This will create a new Medusa plugin project in the `my-plugin` directory.
### Plugin Directory Structure
After the installation is done, the plugin structure will look like this:
![Directory structure of a plugin project](https://res.cloudinary.com/dza7lstvk/image/upload/v1737019441/Medusa%20Book/project-dir_q4xtri.jpg)
- `src/`: Contains the Medusa customizations.
- `src/admin`: Contains [admin extensions](../../admin/page.mdx).
- `src/api`: Contains [API routes](../../api-routes/page.mdx) and [middlewares](../../api-routes/middlewares/page.mdx). You can add store, admin, or any custom API routes.
- `src/jobs`: Contains [scheduled jobs](../../scheduled-jobs/page.mdx).
- `src/links`: Contains [module links](../../module-links/page.mdx).
- `src/modules`: Contains [modules](../../modules/page.mdx).
- `src/subscribers`: Contains [subscribers](../../events-and-subscribers/page.mdx).
- `src/workflows`: Contains [workflows](../../workflows/page.mdx). You can also add [hooks](../../workflows/add-workflow-hook/page.mdx) under `src/workflows/hooks`.
- `package.json`: Contains the plugin's package information, including general information and dependencies.
- `tsconfig.json`: Contains the TypeScript configuration for the plugin.
---
## 2. Prepare Plugin
Before developing, testing, and publishing your plugin, make sure its name in `package.json` is correct. This is the name you'll use to install the plugin in your Medusa application.
For example:
```json title="package.json"
{
"name": "@myorg/plugin-name",
// ...
}
```
---
## 3. Publish Plugin Locally for Development and Testing
Medusa's CLI tool provides commands to simplify developing and testing your plugin in a local Medusa application. You start by publishing your plugin in the local package registry, then install it in your Medusa application. You can then watch for changes in the plugin as you develop it.
### Publish and Install Local Package
<Prerequisites
items={[
{
text: "Medusa application installed.",
link: "/learn/installation",
}
]}
/>
The first time you create your plugin, you need to publish the package into a local package registry, then install it in your Medusa application. This is a one-time only process.
To publish the plugin to the local registry, run the following command in your plugin project:
```bash title="Plugin project"
npx medusa plugin:publish
```
This command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in `package.json`.
Next, navigate to your Medusa application:
```bash title="Medusa application"
cd ~/path/to/medusa-app
```
Make sure to replace `~/path/to/medusa-app` with the path to your Medusa application.
Then, if your project was created before v2.3.1 of Medusa, make sure to install `yalc` as a development dependency:
```bash npm2yarn title="Medusa application"
npm install --save-dev yalc
```
After that, run the following Medusa CLI command to install the plugin:
```bash title="Medusa application"
npx medusa plugin:add @myorg/plugin-name
```
Make sure to replace `@myorg/plugin-name` with the name of your plugin as specified in `package.json`. Your plugin will be installed from the local package registry into your Medusa application.
### Register Plugin in Medusa Application
After installing the plugin, you need to register it in your Medusa application in the configurations defined in `medusa-config.ts`.
Add the plugin to the `plugins` array in the `medusa-config.ts` file:
export const pluginHighlights = [
["5", `"@myorg/plugin-name"`, "Replace with your plugin name."],
]
```ts title="medusa-config.ts" highlights={pluginHighlights}
module.exports = defineConfig({
// ...
plugins: [
{
resolve: "@myorg/plugin-name",
options: {},
},
],
})
```
The `plugins` configuration is an array of objects where each object has a `resolve` key whose value is the name of the plugin package.
#### Pass Module Options through Plugin
Each plugin configuration also accepts an `options` property, whose value is an object of options to pass to the plugin's modules.
For example:
export const pluginOptionsHighlight = [
["6", "options", "Options to pass to the plugin's modules."]
]
```ts title="medusa-config.ts" highlights={pluginOptionsHighlight}
module.exports = defineConfig({
// ...
plugins: [
{
resolve: "@myorg/plugin-name",
options: {
apiKey: true,
},
},
],
})
```
The `options` property in the plugin configuration is passed to all modules in the plugin. Learn more about module options in [this chapter](../../modules/options/page.mdx).
### Watch Plugin Changes During Development
While developing your plugin, you can watch for changes in the plugin and automatically update the plugin in the Medusa application using it. This is the only command you'll continuously need during your plugin development.
To do that, run the following command in your plugin project:
```bash title="Plugin project"
npx medusa plugin:develop
```
This command will:
- Watch for changes in the plugin. Whenever a file is changed, the plugin is automatically built.
- Publish the plugin changes to the local package registry. This will automatically update the plugin in the Medusa application using it. You can also benefit from real-time HMR updates of admin extensions.
### Start Medusa Application
You can start your Medusa application's development server to test out your plugin:
```bash npm2yarn title="Medusa application"
npm run dev
```
While your Medusa application is running and the plugin is being watched, you can test your plugin while developing it in the Medusa application.
---
## 4. Create Customizations in the Plugin
You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin.
<CardList
items={[
{
text: "Create a module",
link: "/learn/fundamentals/modules",
},
{
text: "Create a module link",
link: "/learn/fundamentals/module-links",
},
{
text: "Create a workflow",
link: "/learn/fundamentals/workflows",
},
{
text: "Add a workflow hook",
link: "/learn/fundamentals/workflows/add-workflow-hook",
},
{
text: "Create an API route",
link: "/learn/fundamentals/api-routes",
},
{
text: "Add a subscriber",
link: "/learn/fundamentals/events-and-subscribers",
},
{
text: "Add a scheduled job",
link: "/learn/fundamentals/scheduled-jobs",
},
{
text: "Add an admin widget",
link: "/learn/fundamentals/admin/widgets",
},
{
text: "Add an admin UI route",
link: "/learn/fundamentals/admin/ui-routes",
}
]}
className="mb-1.5"
/>
While building those customizations, you can test them in your Medusa application by [watching the plugin changes](#watch-plugin-changes-during-development) and [starting the Medusa application](#start-medusa-application).
### Generating Migrations for Modules
During your development, you may need to generate migrations for modules in your plugin. To do that, use the `plugin:db:generate` command:
```bash title="Plugin project"
npx medusa plugin:db:generate
```
This command generates migrations for all modules in the plugin. You can then run these migrations on the Medusa application that the plugin is installed in using the `db:migrate` command:
```bash title="Medusa application"
npx medusa db:migrate
```
---
## 5. Publish Plugin to NPM
Medusa's CLI tool provides a command that bundles your plugin to be published to npm. Once you're ready to publish your plugin publicly, run the following command in your plugin project:
```bash
npx medusa plugin:build
```
The command will compile an output in the `.medusa/server` directory.
You can now publish the plugin to npm using the [NPM CLI tool](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Run the following command to publish the plugin to npm:
```bash
npm publish
```
If you haven't logged in before with your NPM account, you'll be asked to log in first. Then, your package is published publicly to be used in any Medusa application.
### Install Public Plugin in Medusa Application
You install a plugin that's published publicly using your package manager. For example:
```bash npm2yarn
npm install @myorg/plugin-name
```
Where `@myorg/plugin-name` is the name of your plugin as published on NPM.
Then, register the plugin in your Medusa application's configurations as explained in [this section](#register-plugin-in-medusa-application).
---
## Update a Published Plugin
If you've published a plugin and you've made changes to it, you'll have to publish the update to NPM again.
First, run the following command to change the version of the plugin:
```bash
npm version <type>
```
Where `<type>` indicates the type of version update youre publishing. For example, it can be `major` or `minor`. Refer to the [npm version documentation](https://docs.npmjs.com/cli/v10/commands/npm-version) for more information.
Then, re-run the same commands for publishing a plugin:
```bash
npx medusa plugin:build
npm publish
```
This will publish an updated version of your plugin under a new version.
@@ -0,0 +1,62 @@
export const metadata = {
title: `${pageNumber} Plugins`,
}
# {metadata.title}
In this chapter, you'll learn what a plugin is in Medusa.
<Note>
Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
</Note>
## What is a Plugin?
A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. The supported customizations are [Modules](../modules/page.mdx), [API Routes](../api-routes/page.mdx), [Workflows](../workflows/page.mdx), [Workflow Hooks](../workflows/workflow-hooks/page.mdx), [Links](../module-links/page.mdx), [Subscribers](../events-and-subscribers/page.mdx), [Scheduled Jobs](../scheduled-jobs/page.mdx), and [Admin Extensions](../admin/page.mdx).
Plugins allow you to reuse your Medusa customizations across multiple projects or share them with the community. They can be published to npm and installed in any Medusa project.
![Diagram showcasing a wishlist plugin installed in a Medusa application](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540762/Medusa%20Book/plugin-diagram_oepiis.jpg)
<Note title="Tip">
Learn how to create a wishlist plugin in [this guide](!resources!/plugins/guides/wishlist).
</Note>
---
## Plugin vs Module
A [module](../modules/page.mdx) is an isolated package related to a single domain or functionality, such as product reviews or integrating a Content Management System. A module can't access any resources in the Medusa application that are outside its codebase.
A plugin, on the other hand, can contain multiple Medusa customizations, including modules. Your plugin can define a module, then build flows around it.
For example, in a plugin, you can define a module that integrates a third-party service, then add a workflow that uses the module when a certain event occurs to sync data to that service.
<Note title="Use a plugin if" type="success">
- You want to reuse your Medusa customizations across multiple projects.
- You want to share your Medusa customizations with the community.
</Note>
<Note title="Don't use a plugin if" type="error">
- You want to build a custom feature related to a single domain or integrate a third-party service. Instead, use a [module](../modules/page.mdx). You can wrap that module in a plugin if it's used in other customizations, such as if it has a module link or it's used in a workflow.
</Note>
---
## How to Create a Plugin?
The next chapter explains how you can create and publish a plugin.
---
## Plugin Guides and Resources
For more resources and guides related to plugins, refer to the [Resources documentation](!resources!/plugins).
@@ -17,15 +17,25 @@ In a common Medusa application, requests go through four layers in the stack. In
3. Modules: Workflows use domain-specific modules for resource management.
3. Data store: Modules query the underlying datastore, which is a PostgreSQL database in common cases.
<Note>
These layers of stack can be implemented within [plugins](../../fundamentals/plugins/page.mdx).
</Note>
![Diagram illustrating the HTTP layer](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175296/Medusa%20Book/http-layer_sroafr.jpg)
---
## Database Layer
The Medusa application injects into each module a connection to the configured PostgreSQL database.
The Medusa application injects into each module a connection to the configured PostgreSQL database. Modules use that connection to read and write data to the database.
Modules use that connection to read and write data to the database.
<Note>
Modules can be implemented within [plugins](../../fundamentals/plugins/page.mdx).
</Note>
![Diagram illustrating the database layer](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175379/Medusa%20Book/db-layer_pi7tix.jpg)
@@ -33,19 +43,23 @@ Modules use that connection to read and write data to the database.
## Service Integrations
Third-party services are integrated through commerce and architectural modules.
Third-party services are integrated through commerce and architectural modules. You also create custom third-party integrations through a custom module.
You also create custom third-party integrations through a custom module.
<Note>
Modules can be implemented within [plugins](../../fundamentals/plugins/page.mdx).
</Note>
### Commerce Modules
Commerce modules integrate third-party services relevant for commerce or user-facing features. For example, you integrate Stripe through a payment module provider.
[Commerce modules](!resources!/commerce-modules) integrate third-party services relevant for commerce or user-facing features. For example, you integrate Stripe through a payment module provider.
![Diagram illustrating the commerce modules integration to third-party services](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175357/Medusa%20Book/service-commerce_qcbdsl.jpg)
### Architectural Modules
Architectural modules integrate third-party services and systems for architectural features. For example, you integrate Redis as a pub/sub service to send events, or SendGrid to send notifications.
[Architectural modules](!resources!/architectural-modules) integrate third-party services and systems for architectural features. For example, you integrate Redis as a pub/sub service to send events, or SendGrid to send notifications.
![Diagram illustrating the architectural modules integration to third-party services and systems](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175342/Medusa%20Book/service-arch_ozvryw.jpg)
+16 -13
View File
@@ -32,15 +32,15 @@ export const generatedEditDates = {
"app/learn/fundamentals/data-models/default-properties/page.mdx": "2024-10-21T13:30:21.368Z",
"app/learn/fundamentals/workflows/advanced-example/page.mdx": "2024-09-11T10:46:59.975Z",
"app/learn/fundamentals/events-and-subscribers/emit-event/page.mdx": "2024-11-25T16:19:32.168Z",
"app/learn/fundamentals/workflows/conditions/page.mdx": "2024-12-11T08:44:00.239Z",
"app/learn/fundamentals/workflows/conditions/page.mdx": "2025-01-27T08:45:19.027Z",
"app/learn/fundamentals/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
"app/learn/fundamentals/admin/page.mdx": "2024-10-23T07:08:55.898Z",
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2024-12-04T07:37:59.822Z",
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2025-01-27T08:45:19.028Z",
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2024-12-12T15:09:41.179Z",
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2024-11-11T15:27:59.794Z",
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2024-12-12T14:23:26.254Z",
"app/learn/fundamentals/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
"app/learn/fundamentals/modules/options/page.mdx": "2024-11-19T16:37:47.253Z",
"app/learn/fundamentals/modules/options/page.mdx": "2025-01-16T09:21:38.244Z",
"app/learn/fundamentals/data-models/relationships/page.mdx": "2024-12-12T14:08:50.686Z",
"app/learn/fundamentals/workflows/compensation-function/page.mdx": "2024-12-06T14:34:50.384Z",
"app/learn/fundamentals/modules/service-factory/page.mdx": "2024-10-21T13:30:21.371Z",
@@ -50,7 +50,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx": "2024-10-21T13:30:21.371Z",
"app/learn/fundamentals/api-routes/parameters/page.mdx": "2024-11-19T16:37:47.251Z",
"app/learn/fundamentals/api-routes/http-methods/page.mdx": "2024-10-21T13:30:21.367Z",
"app/learn/fundamentals/admin/tips/page.mdx": "2024-12-12T11:43:26.003Z",
"app/learn/fundamentals/admin/tips/page.mdx": "2025-01-27T08:45:19.023Z",
"app/learn/fundamentals/api-routes/cors/page.mdx": "2024-12-09T13:04:04.357Z",
"app/learn/fundamentals/admin/ui-routes/page.mdx": "2024-12-09T16:44:40.198Z",
"app/learn/fundamentals/api-routes/middlewares/page.mdx": "2024-12-09T13:04:03.712Z",
@@ -78,18 +78,18 @@ export const generatedEditDates = {
"app/learn/fundamentals/module-links/query/page.mdx": "2024-12-09T15:54:44.798Z",
"app/learn/fundamentals/modules/db-operations/page.mdx": "2024-12-09T14:40:50.581Z",
"app/learn/fundamentals/modules/multiple-services/page.mdx": "2024-10-21T13:30:21.370Z",
"app/learn/fundamentals/modules/page.mdx": "2024-12-09T15:55:25.858Z",
"app/learn/fundamentals/modules/page.mdx": "2025-01-16T08:34:28.947Z",
"app/learn/debugging-and-testing/instrumentation/page.mdx": "2024-12-09T15:33:05.121Z",
"app/learn/fundamentals/api-routes/additional-data/page.mdx": "2025-01-23T15:54:44.613Z",
"app/learn/fundamentals/workflows/variable-manipulation/page.mdx": "2024-12-09T15:57:54.506Z",
"app/learn/fundamentals/api-routes/additional-data/page.mdx": "2025-01-27T08:45:19.025Z",
"app/learn/fundamentals/workflows/variable-manipulation/page.mdx": "2025-01-27T08:45:19.029Z",
"app/learn/customization/custom-features/api-route/page.mdx": "2024-12-09T10:39:30.046Z",
"app/learn/customization/custom-features/module/page.mdx": "2024-12-09T14:36:02.100Z",
"app/learn/customization/custom-features/workflow/page.mdx": "2024-12-09T14:36:29.482Z",
"app/learn/customization/extend-features/extend-create-product/page.mdx": "2025-01-06T11:18:58.250Z",
"app/learn/customization/custom-features/page.mdx": "2024-12-09T10:46:28.593Z",
"app/learn/customization/customize-admin/page.mdx": "2024-12-09T11:02:38.801Z",
"app/learn/customization/customize-admin/route/page.mdx": "2025-01-22T16:23:31.772Z",
"app/learn/customization/customize-admin/widget/page.mdx": "2024-12-09T11:02:39.108Z",
"app/learn/customization/customize-admin/route/page.mdx": "2025-01-27T08:45:19.021Z",
"app/learn/customization/customize-admin/widget/page.mdx": "2025-01-27T08:45:19.022Z",
"app/learn/customization/extend-features/define-link/page.mdx": "2024-12-09T11:02:39.346Z",
"app/learn/customization/extend-features/page.mdx": "2024-12-09T11:02:39.244Z",
"app/learn/customization/extend-features/query-linked-records/page.mdx": "2024-12-09T11:02:39.519Z",
@@ -99,16 +99,19 @@ export const generatedEditDates = {
"app/learn/customization/integrate-systems/service/page.mdx": "2024-12-09T11:02:39.594Z",
"app/learn/customization/next-steps/page.mdx": "2024-12-06T14:34:53.356Z",
"app/learn/fundamentals/modules/architectural-modules/page.mdx": "2024-10-21T13:30:21.367Z",
"app/learn/introduction/architecture/page.mdx": "2024-10-21T13:30:21.368Z",
"app/learn/introduction/architecture/page.mdx": "2025-01-16T10:25:10.780Z",
"app/learn/fundamentals/data-models/infer-type/page.mdx": "2024-12-09T15:54:08.713Z",
"app/learn/fundamentals/custom-cli-scripts/seed-data/page.mdx": "2024-12-09T14:38:06.385Z",
"app/learn/fundamentals/environment-variables/page.mdx": "2024-12-09T11:00:57.428Z",
"app/learn/build/page.mdx": "2024-12-09T11:05:17.383Z",
"app/learn/deployment/general/page.mdx": "2024-11-25T14:33:50.439Z",
"app/learn/fundamentals/workflows/multiple-step-usage/page.mdx": "2024-11-25T16:19:32.169Z",
"app/learn/installation/page.mdx": "2025-01-06T09:12:48.690Z",
"app/learn/installation/page.mdx": "2025-01-27T08:45:19.029Z",
"app/learn/fundamentals/data-models/check-constraints/page.mdx": "2024-12-06T14:34:50.384Z",
"app/learn/fundamentals/module-links/link/page.mdx": "2025-01-06T09:27:25.604Z",
"app/learn/fundamentals/workflows/store-executions/page.mdx": "2025-01-24T12:09:24.087Z",
"app/learn/update/page.mdx": "2025-01-24T17:35:21.335Z"
"app/learn/fundamentals/plugins/create/page.mdx": "2025-01-22T10:14:47.933Z",
"app/learn/fundamentals/plugins/page.mdx": "2025-01-22T10:14:10.433Z",
"app/learn/customization/reuse-customizations/page.mdx": "2025-01-22T10:01:57.665Z",
"app/learn/fundamentals/workflows/store-executions/page.mdx": "2025-01-27T08:45:19.028Z",
"app/learn/update/page.mdx": "2025-01-27T08:45:19.030Z"
}
+32 -4
View File
@@ -181,6 +181,15 @@ export const generatedSidebar = [
],
"chapterTitle": "2.4. Integrate Systems"
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"title": "Re-Use Customizations",
"path": "/learn/customization/reuse-customizations",
"children": [],
"chapterTitle": "2.5. Re-Use Customizations"
},
{
"loaded": true,
"isPathHref": true,
@@ -188,7 +197,7 @@ export const generatedSidebar = [
"title": "Next Steps",
"path": "/learn/customization/next-steps",
"children": [],
"chapterTitle": "2.5. Next Steps"
"chapterTitle": "2.6. Next Steps"
}
],
"chapterTitle": "2. Customize"
@@ -792,6 +801,25 @@ export const generatedSidebar = [
],
"chapterTitle": "3.9. Admin Development"
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/plugins",
"title": "Plugins",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/plugins/create",
"title": "Create Plugin",
"children": [],
"chapterTitle": "3.10.1. Create Plugin"
}
],
"chapterTitle": "3.10. Plugins"
},
{
"loaded": true,
"isPathHref": true,
@@ -806,10 +834,10 @@ export const generatedSidebar = [
"path": "/learn/fundamentals/custom-cli-scripts/seed-data",
"title": "Seed Data",
"children": [],
"chapterTitle": "3.10.1. Seed Data"
"chapterTitle": "3.11.1. Seed Data"
}
],
"chapterTitle": "3.10. Custom CLI Scripts"
"chapterTitle": "3.11. Custom CLI Scripts"
},
{
"loaded": true,
@@ -818,7 +846,7 @@ export const generatedSidebar = [
"title": "Environment Variables",
"path": "/learn/fundamentals/environment-variables",
"children": [],
"chapterTitle": "3.11. Environment Variables"
"chapterTitle": "3.12. Environment Variables"
}
],
"chapterTitle": "3. Fundamentals"
+17
View File
@@ -106,6 +106,11 @@ export const sidebar = sidebarAttachHrefCommonOptions([
},
],
},
{
type: "link",
title: "Re-Use Customizations",
path: "/learn/customization/reuse-customizations",
},
{
type: "link",
title: "Next Steps",
@@ -458,6 +463,18 @@ export const sidebar = sidebarAttachHrefCommonOptions([
},
],
},
{
type: "link",
path: "/learn/fundamentals/plugins",
title: "Plugins",
children: [
{
type: "link",
path: "/learn/fundamentals/plugins/create",
title: "Create Plugin",
},
],
},
{
type: "link",
path: "/learn/fundamentals/custom-cli-scripts",
@@ -15,7 +15,7 @@ export const metadata = {
# {metadata.title}
The `create-medusa-app` CLI tool simplifies the process of creating a new Medusa project and provides an onboarding experience.
The `create-medusa-app` CLI tool simplifies the process of creating a new Medusa project. It also allows you to setup a [Medusa plugin project](#create-a-medusa-plugin-project).
<Prerequisites items={[
{
@@ -206,6 +206,23 @@ npx create-medusa-app@latest [project-name]
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`--plugin`
</Table.Cell>
<Table.Cell>
Create a [plugin project](#create-a-medusa-plugin-project) instead of a Medusa application. This option is available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
</Table.Cell>
<Table.Cell>
`false`
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
@@ -241,6 +258,18 @@ If the database already has the necessary migrations and you don't need the comm
---
## Create a Medusa Plugin Project
The `create-medusa-app` tool can also be used to create a Medusa Plugin Project. You can do that by passing the `--plugin` option:
```bash
npx create-medusa-app@latest my-plugin --plugin
```
Learn more about how to create a plugin in [this documentation](!docs!/learn/fundamentals/plugins).
---
## Troubleshooting
<DetailsList
@@ -1,6 +1,6 @@
---
sidebar_label: "exec"
sidebar_position: 6
sidebar_position: 7
---
import { Table } from "docs-ui"
@@ -0,0 +1,100 @@
---
sidebar_label: "plugin"
sidebar_position: 6
---
import { Table } from "docs-ui"
export const metadata = {
title: `plugin Commands - Medusa CLI Reference`,
}
# {metadata.title}
Commands starting with `plugin:` perform actions related to [plugin](!docs!/learn/fundamentals/plugins) development.
<Note>
These commands are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
</Note>
## plugin\:publish
Publish a plugin into the local packages registry. The command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. You can then install the plugin in a local Medusa project using the [plugin\:add](#pluginadd) command.
```bash
npx medusa plugin:publish
```
---
## plugin\:add
Install the specified plugins from the local package registry into a local Medusa application. Plugins can be added to the local package registry using the [plugin\:publish](#pluginpublish) command.
```bash
npx medusa plugin:add [names...]
```
### Arguments
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Argument</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
<Table.HeaderCell>Required</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`names`
</Table.Cell>
<Table.Cell>
The names of one or more plugins to install from the local package registry. A plugin's name is as specified in its `package.json` file.
</Table.Cell>
<Table.Cell>
Yes
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
---
## plugin\:develop
Start a development server for a plugin. The command will watch for changes in the plugin's source code and automatically re-publish the changes into the local package registry.
```bash
npx medusa plugin:develop
```
---
## plugin\:db\:generate
Generate migrations for all modules in a plugin.
```bash
npx medusa plugin:db:generate
```
---
## plugin\:build
Build a plugin before publishing it to NPM. The command will compile an output in the `.medusa/server` directory.
```bash
npx medusa plugin:build
```
@@ -1,6 +1,6 @@
---
sidebar_label: "start-cluster"
sidebar_position: 7
sidebar_position: 8
---
import { Table } from "docs-ui"
@@ -1,6 +1,6 @@
---
sidebar_label: "telemetry"
sidebar_position: 8
sidebar_position: 9
---
import { Table } from "docs-ui"
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
import { ChildDocs } from "docs-ui"
export const metadata = {
title: `Plugins`,
}
# {metadata.title}
A plugin is a package of re-usable Medusa customizations, including [modules](!docs!/learn/fundamentals/modules), [workflows](!docs!/learn/fundamentals/workflows), [API routes](!docs!/learn/fundamentals/api-routes), and more.
Plugins are useful if you want to re-use your customizations across Medusa applications, or you want to share those customizations with the community. You publish your plugin to NPM, then install it in a Medusa application to use its features.
<Note>
Learn more about plugins and their difference from modules in [this documentation](!docs!/learn/fundamentals/plugins).
</Note>
## Guides
<ChildDocs showItems={["Guides"]} hideTitle />
@@ -212,7 +212,7 @@ So, the `RestockModuleService` class now has methods like `createRestockSubscrip
<Note>
Find all methods generated by the `MedusaService` in [this reference](../../..//service-factory-reference/page.mdx).
Find all methods generated by the `MedusaService` in [this reference](../../../service-factory-reference/page.mdx).
</Note>
@@ -330,7 +330,7 @@ You define a link using `defineLink` from the Modules SDK. It accepts three para
1. The first data model part of the link, which is the Restock Module's `restockSubscription` data model. A module has a special `linkable` property that contain link configurations for its data models. You also specify the field that points to the product variant.
1. The second data model part of the link, which is the Product Module's `productVariant` data model.
3. An object of configurations for the module link. By default, Medusa creates a table in the database to represent the link you define. However, in this guide, you only want this link to retrieve the variants associated with a subscription and vice-versa. So, you enable `readOnly` telling Medusa not to create a table for this link.
3. An object of configurations for the module link. By default, Medusa creates a table in the database to represent the link you define. However, in this guide, you only want this link to retrieve the variants associated with a subscription. So, you enable `readOnly` telling Medusa not to create a table for this link.
In the next steps, you'll see how this link allows you to retrieve product variants' details when retrieving restock subscriptions.
+8 -5
View File
@@ -100,7 +100,7 @@ export const generatedEditDates = {
"app/commerce-modules/user/page.mdx": "2025-01-09T13:41:05.543Z",
"app/commerce-modules/page.mdx": "2024-12-23T14:38:21.064Z",
"app/contribution-guidelines/docs/page.mdx": "2024-12-12T11:06:12.250Z",
"app/create-medusa-app/page.mdx": "2025-01-06T09:14:55.483Z",
"app/create-medusa-app/page.mdx": "2025-01-16T10:00:25.975Z",
"app/deployment/admin/vercel/page.mdx": "2024-10-16T08:10:29.377Z",
"app/deployment/medusa-application/railway/page.mdx": "2024-11-11T11:50:10.517Z",
"app/deployment/storefront/vercel/page.mdx": "2025-01-06T12:19:31.142Z",
@@ -593,11 +593,11 @@ export const generatedEditDates = {
"references/types/types.NotificationTypes/page.mdx": "2024-11-25T17:49:28.027Z",
"app/medusa-cli/commands/db/page.mdx": "2025-01-16T07:34:08.014Z",
"app/medusa-cli/commands/develop/page.mdx": "2024-08-28T10:43:45.452Z",
"app/medusa-cli/commands/exec/page.mdx": "2024-08-28T10:45:31.229Z",
"app/medusa-cli/commands/exec/page.mdx": "2025-01-16T09:51:17.050Z",
"app/medusa-cli/commands/new/page.mdx": "2024-08-28T10:43:34.110Z",
"app/medusa-cli/commands/start-cluster/page.mdx": "2024-08-28T11:25:05.257Z",
"app/medusa-cli/commands/start-cluster/page.mdx": "2025-01-16T09:51:19.838Z",
"app/medusa-cli/commands/start/page.mdx": "2024-08-28T10:44:19.952Z",
"app/medusa-cli/commands/telemtry/page.mdx": "2024-08-28T11:25:08.553Z",
"app/medusa-cli/commands/telemtry/page.mdx": "2025-01-16T09:51:24.323Z",
"app/medusa-cli/commands/user/page.mdx": "2024-08-28T10:44:52.489Z",
"app/recipes/marketplace/examples/restaurant-delivery/page.mdx": "2024-12-11T10:05:52.851Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminCreateCustomerGroup/page.mdx": "2024-12-09T13:21:33.569Z",
@@ -5609,7 +5609,7 @@ export const generatedEditDates = {
"references/modules/sales_channel_models/page.mdx": "2024-12-10T14:55:13.205Z",
"references/types/DmlTypes/types/types.DmlTypes.KnownDataTypes/page.mdx": "2024-12-17T16:57:19.922Z",
"references/types/DmlTypes/types/types.DmlTypes.RelationshipTypes/page.mdx": "2024-12-10T14:54:55.435Z",
"app/recipes/commerce-automation/restock-notification/page.mdx": "2024-12-11T08:47:27.471Z",
"app/recipes/commerce-automation/restock-notification/page.mdx": "2025-01-23T10:18:28.126Z",
"app/troubleshooting/workflow-errors/page.mdx": "2024-12-11T08:44:36.598Z",
"app/integrations/guides/shipstation/page.mdx": "2025-01-07T14:40:42.561Z",
"app/nextjs-starter/guides/customize-stripe/page.mdx": "2024-12-25T14:48:55.877Z",
@@ -5866,6 +5866,7 @@ export const generatedEditDates = {
"references/types/types/types.CreateProductVariantWorkflowInputDTO/page.mdx": "2025-01-13T17:30:29.301Z",
"references/types/types/types.CreateProductWorkflowInputDTO/page.mdx": "2025-01-13T17:30:29.302Z",
"references/types/types/types.UpdateProductVariantWorkflowInputDTO/page.mdx": "2025-01-13T17:30:29.302Z",
"app/medusa-cli/commands/plugin/page.mdx": "2025-01-16T09:58:16.179Z",
"references/inventory_next/interfaces/inventory_next.UpdateInventoryLevelInput/page.mdx": "2025-01-17T16:43:28.933Z",
"references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemLocationLevels/page.mdx": "2025-01-17T16:43:31.720Z",
"references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemsLocationLevels/page.mdx": "2025-01-17T16:43:31.721Z",
@@ -5887,5 +5888,7 @@ export const generatedEditDates = {
"references/core_flows/types/core_flows.ThrowUnlessPaymentCollectionNotePaidInput/page.mdx": "2025-01-17T16:43:25.819Z",
"references/core_flows/types/core_flows.ValidatePaymentsRefundStepInput/page.mdx": "2025-01-17T16:43:26.128Z",
"references/core_flows/types/core_flows.ValidateRefundStepInput/page.mdx": "2025-01-17T16:43:26.124Z",
"app/plugins/guides/wishlist/page.mdx": "2025-01-23T11:59:10.008Z",
"app/plugins/page.mdx": "2025-01-22T09:36:37.745Z",
"app/admin-components/components/data-table/page.mdx": "2025-01-22T16:01:01.279Z"
}
@@ -823,6 +823,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/medusa-cli/commands/new/page.mdx",
"pathname": "/medusa-cli/commands/new"
},
{
"filePath": "/www/apps/resources/app/medusa-cli/commands/plugin/page.mdx",
"pathname": "/medusa-cli/commands/plugin"
},
{
"filePath": "/www/apps/resources/app/medusa-cli/commands/start/page.mdx",
"pathname": "/medusa-cli/commands/start"
@@ -863,6 +867,14 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/page.mdx",
"pathname": "/"
},
{
"filePath": "/www/apps/resources/app/plugins/guides/wishlist/page.mdx",
"pathname": "/plugins/guides/wishlist"
},
{
"filePath": "/www/apps/resources/app/plugins/page.mdx",
"pathname": "/plugins"
},
{
"filePath": "/www/apps/resources/app/recipes/b2b/page.mdx",
"pathname": "/recipes/b2b"
+44
View File
@@ -15942,6 +15942,41 @@ export const generatedSidebar = [
}
]
},
{
"loaded": true,
"isPathHref": true,
"type": "ref",
"path": "/plugins",
"title": "Plugins",
"isChildSidebar": true,
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"title": "Overview",
"path": "/plugins",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Guides",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"title": "Wishlist",
"path": "/plugins/guides/wishlist",
"description": "Learn how to build a wishlist plugin.",
"children": []
}
]
}
]
},
{
"loaded": true,
"isPathHref": true,
@@ -16456,6 +16491,15 @@ export const generatedSidebar = [
"description": "",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/medusa-cli/commands/plugin",
"title": "plugin",
"description": "",
"children": []
},
{
"loaded": true,
"isPathHref": true,
+8
View File
@@ -6,6 +6,7 @@ import { currencySidebar } from "./sidebars/currency.mjs"
import { customerSidebar } from "./sidebars/customer.mjs"
import { fulfillmentSidebar } from "./sidebars/fulfillment.mjs"
import { integrationsSidebar } from "./sidebars/integrations.mjs"
import { pluginsSidebar } from "./sidebars/plugins.mjs"
import { inventorySidebar } from "./sidebars/inventory.mjs"
import { orderSidebar } from "./sidebars/order-module.mjs"
import { paymentSidebar } from "./sidebars/payment.mjs"
@@ -87,6 +88,13 @@ export const sidebar = sidebarAttachHrefCommonOptions([
isChildSidebar: true,
children: integrationsSidebar,
},
{
type: "ref",
path: "/plugins",
title: "Plugins",
isChildSidebar: true,
children: pluginsSidebar,
},
{
type: "link",
path: "/storefront-development",
+20
View File
@@ -0,0 +1,20 @@
/** @type {import('types').RawSidebarItem[]} */
export const pluginsSidebar = [
{
type: "link",
title: "Overview",
path: "/plugins",
},
{
type: "category",
title: "Guides",
children: [
{
type: "link",
title: "Wishlist",
path: "/plugins/guides/wishlist",
description: "Learn how to build a wishlist plugin.",
},
],
},
]
@@ -8,6 +8,7 @@ import { WorkflowDiagramLegend } from "../Common/Legend"
export const WorkflowDiagramList = ({
workflow,
hideLegend = false,
}: WorkflowDiagramCommonProps) => {
const clusters = createNodeClusters(workflow.steps)
@@ -20,7 +21,7 @@ export const WorkflowDiagramList = ({
<WorkflowDiagramListDepth cluster={cluster} next={next} key={depth} />
)
})}
<WorkflowDiagramLegend />
{!hideLegend && <WorkflowDiagramLegend />}
</div>
)
}
@@ -6,9 +6,13 @@ import { Loading } from "../.."
import { WorkflowDiagramCanvas } from "./Canvas"
import { WorkflowDiagramList } from "./List"
export type WorkflowDiagramCommonOptionsProps = {
hideLegend?: boolean
}
export type WorkflowDiagramCommonProps = {
workflow: Workflow
}
} & WorkflowDiagramCommonOptionsProps
export type WorkflowDiagramType = "canvas" | "list"