Production GatsbyJS: Plugins
Suggested reading: Production GatsbyJS: API Files
const pluginsReducer = (acc, [resolve, value]) => {
if (value === true) return [...acc, resolve];
if (value.constructor && value.constructor.name === "Object")
return [...acc, { resolve, options: value }];
if (Array.isArray(value))
return [...acc, ...value.map(options => ({ resolve, options }))];
return acc;
};
module.exports = Object.entries(plugins).reduce(pluginsReducer, []);
Problem
Gatsby was designed to use two types in the gatsby-config
plugins array:
- A
String
like"gatsby-plugin-react-helmet"
- An
Object
like{ resolve: "gatsby-source-filesystem", options: /* Some Options */ }
When using 10+ plugins it can make configs hard to read. This is especially the case when you have the same plugin multiple times in the plugins array, as is commonly the case with gatsby-source-filesystem
.
Solution
In an ideal world the plugins array would be an object keyed by the name of the plugin. This way, there would be only one key per plugin.
Returning an array
The result must match what GatsbyJS expects, so the plugins
object that we're going to make must be transformed to an array.
Object.entries(plugins).reduce(pluginsReducer, []);
Generally, Object.keys
, Object.values
, or Object.entries
can be used to convert an object into an array. In this case, both the keys and values of the object are needed, so Object.entries
is the way to go. The array returned is still not formatted correctly, so an Array.prototype.reduce
is needed. I'll get to that a bit later.
String type plugin declarations
{
"gatsby-plugin-react-helmet": true
}
Easy enough. What is the most useful thing to put as the value. It's possible to not care about the value and assume that if the key exists, the plugin is used. However, a boolean value could act as a switch for enabling or disabling plugins.
Object type plugin declarations
{
"gatsby-source-filesystem": {
name: `pages`,
path: `${__dirname}/src/pages/`
}
}
Since every object type plugin declaration has only two keys: resolve
and options
, they can be respectively split into key-value pairs.
Combining multiple plugin declarations with the same resolver.
If the key must remain constant, the only option is to have an array of options objects.
Code Review
String type declarations
if (value === true) return [...acc, resolve];
If the value is exactly true, append the key (a.k.a resolve
in the GatsbyJS plugins array)
Object type declarations
if (value.constructor && value.constructor.name === "Object")
return [...acc, { resolve, options: value }];
If the value is an Object
, append the { resolve, options }
object that GatsbyJS expects.
Object type declarations with multiple options objects
if (Array.isArray(value))
return [...acc, ...value.map(options => ({ resolve, options }))];
This one is a little different, concatenating an array of { resolve, options }
objects onto the accumulator.
Default fallback case
return acc;
If something in the object entries is not an Object
, Array
, or true
, it should be rejected.
Example
Here's something that is in the plugins config for bayanbennett.com.
"gatsby-plugin-schema-snapshot": NODE_ENV !== "production" && {
path: "gatsby.graphql",
update: true,
},
"gatsby-transformer-sharp": true,
There's something extra here: NODE_ENV !== "production"
. This allows developers to enable or disable plugins based on the node environment. In this example, when the NODE_ENV
is "production"
, the gastby-plugin-schema-snapshot
plugin is not included in the build.
Conclusion
If something isn't working, feel free to play with it and get it to what makes the most sense for the project. Worst case, falling back to the official method is always an option.
I have found that this method for declaring plugins has been useful to me on a few projects and I don't have a good reason to depart from it. My gatsby-config/plugins
are much easier to read and therefore much easier to maintain.