GraphCommerce's plugin system allows you to extend or replace GraphCommerce's built-in components or functions with your own logic.
A plugin is a way to modify React Components or a Function by wrapping them, without having to modify the code directly.
For the M2 people: Think of around plugins, but without configuration files and no performance penalty.
GraphCommerce has three kinds of plugins, component plugins, function plugins and replacement plugins.
React Component plugins, which can be used to:
Function plugins, which can be used to:
Replacement plugins, which can be used to:
In this example we're going to add some text to list items, just like the text ‘BY GC’ that can seen in the demo on category pages.
Create a new file in /plugins/MyProductListItemPlugin.tsx
with the following
contents:
import type { ProductListItemProps } from '@graphcommerce/magento-product'
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
import { Typography } from '@mui/material'
export const config: PluginConfig = {
type: 'component',
module: '@graphcommerce/magento-product',
}
// Exported name should be the same as the function you want to create a plugin for
export const ProductListItem = (props: PluginProps<ProductListItemProps>) => {
// Prev in this case is ProductListItem, you should be able to see this if you log it.
// Prev needs to be rendered and {...rest} always needs to be passed.
const { Prev, ...rest } = props
return (
<Prev
{...rest}
subTitle={
<Typography component='span' variant='caption'>
Plugin!
</Typography>
}
/>
)
}
Create a new file in /plugins/myFunctionPlugin.tsx
with the following
contents:
import type { graphqlConfig as graphqlConfigType } from '@graphcommerce/graphql'
import type { FunctionPlugin, PluginConfig } from '@graphcommerce/next-config'
import { createStoreLink } from '../link/createStoreLink'
export const config: PluginConfig = {
type: 'function',
module: '@graphcommerce/graphql',
}
// Exported name should be the same as the function you want to create a plugin for
export const graphqlConfig: FunctionPlugin<typeof graphqlConfigType> = (
prev,
conf,
) => {
const results = prev(conf)
return {
...results,
links: [...results.links, createStoreLink(conf.storefront.locale)],
}
}
import { ProductCountProps } from '@graphcommerce/magento-product'
import { PluginConfig } from '@graphcommerce/next-config'
export const config: PluginConfig = {
type: 'replace',
module: '@graphcommerce/magento-product',
}
export function ProductListCount(props: ProductCountProps) {
const { total_count } = props
return <div>{total_count}</div>
}
Note: The original component can not be used, because we completely rewrite the export. If you want to do this, a component or function plugin is a better choice.
When creating the plugin for the first time you need to restart your dev server once. After the first generation of the interceptor file, the file is watched and changes will be picked up.
If everything went as expected you should see your plugin applied correct.
You can enable debug mode in your graphcommerce.config.js:
const config = {
debug: { pluginStatus: true },
}
Or use GC_DEBUG_PLUGIN_STATUS=true
in your environment variables.
GraphCommerce uses a custom Webpack plugin to load the plugins. The plugin does
a glob search for plugin folders in each GraphCommerce related pacakge:
${packageLocation}/plugins/**/*.{ts|tsx}
Package locations are the root and all packages with graphcommerce
in the name
(This means all @graphcommerce/*
packages and
@your-company/graphcommerce-plugin-name
)
The Webpack plugin statically analyses the plugin files to find any valid configuration. This is then used to create the interceptors.
Provide an ifConfig in the plugin config to conditionally include a plugin if a configuration value is truthy:
export const config: PluginConfig = {
type: 'component',
module: '@graphcommerce/magento-product',
ifConfig: 'demoMode',
}
Or checking on a value:
export const config: PluginConfig<'compareVariant'> = {
type: 'component',
module: '@graphcommerce/magento-product',
ifConfig: ['compareVariant', 'CHECKBOX'],
}
The plugin loading order is determined by the order of the dependencies defined in the package.json. If the order isn't correct, make sure you've defined the correct dependencies in your package.json.
Local plugins are closest to the original component, meaning that package specific plugins have already been called before your plugin is called.
After the creation of the plugin file GraphCommerce will create an 'interceptor' for your file.
To see the the created plugin for ProductListItem, 'Go to Definition'
(CMD/Ctrl+Click) on <ProductListItem>
in
components/ProductListItems/productListRenderer.tsx
. You should now go to the
ProductListItem.interceptor.tsx
file.
In this file the original ProductListItem
is replaced with
PluginDemoProductListItemInterceptor
. The interceptor renders
<PluginDemoProductListItemSource />
with a Prev
prop which is the plugin.
The whole plugin 'chain' is constructed here and eventually ending up on
ProductListItemOriginal
which is the original component (but renamed).
In the examples above we've extended the product list items, but it should also work for other things such as: