-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Warn when implementing apis twice #3889
Warn when implementing apis twice #3889
Conversation
Deploy preview for gatsbygram ready! Built with commit db3e56d |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweeeeeet!!! This is so needed. Thanks for taking this on!
} | ||
|
||
// Run the specified api in any plugins that have implemented it | ||
const apiRunner = ({ api, args, defaultReturn, checkDupes = false }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like named arguments (should have used them here originally) but we'd need to update the node/browser api runners as well to keep things consistent internally. So could you revert this change for now and perhaps do the named args change in another PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
const dupes = duplicatedApis(plugins, api) | ||
if (dupes.length > 0) { | ||
let m = `\nThe "${api}" api has been implemented multiple times. Only the last implementation will be used.` | ||
let m2 = `This is probably an error, see https://github.com/gatsbyjs/gatsby/issues/2005#issuecomment-326787567 for workarounds.` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a docs page — could you add a new docs page explaining the problem and solutions and link to that instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you already said you were going to do this :-)
|
||
if (checkDupes) { | ||
const dupes = duplicatedApis(plugins, api) | ||
if (dupes.length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you need to filter this list to APIs where duplicates is a problem e.g. replaceRenderer
. Most SSR APIs are fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I think we should suppress warnings for duplicated APIs where there's an implementation in the site's gatsby-* file as those always override other plugins. This is the normal fix — merge plugin's implementations of replaceRenderer
. As things are now, the warning would keep showing which isn't what we want as then the user might uninstall the plugin to fix things but which would actually probably break things as many plugins with gatsby-ssr.js
files also have other API implementations e.g. for browser APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you need to filter this list to APIs where duplicates is a problem
This is handled at the point that apiRunner is called, using the new checkDupes
flag:
// check for duplicate api implementations
apiRunner({api: `replaceRenderer`, args, checkDupes: true})
// don't check for duplicate api implementations (default behaviour)
apiRunner({api: `replaceRenderer`, args})
But actually it's just the replaceRenderer
api that shouldn't be duplicated? For now I can hardcode the name replaceRenderer
within apiRunner
. Then we can extract it out later if necessary.
we should suppress warnings for duplicated APIs where there's an implementation in the site's gatsby-* file
👍
@KyleAMathews thinking a bit more about your comment on when to show an error...
Is it true that - when someone has a couple of The different states that we can detect are like this:
When there's definitely an error, we can show an error message. When there's possibly an error, what should Gatsby do? I guess the options are:
|
@KyleAMathews I made a first draft at some docs, added to this PR. I also created an example repo to check what I was writing as I went along. In that example repo, the final commit should result in a functioning |
|
||
## What is the `replaceRenderer` API? | ||
|
||
The `replaceRenderer` API is one of [Gatsby's Server Side Rendering (SSR) hooks](https://www.gatsbyjs.org/docs/ssr-apis/#replaceRenderer). It's used to customise how Gatsby renders your static content. It can be implemented by any Gatsby plugin or your `gatsby-ssr.js` file - adding support for Redux, CSS-in-JS libraries or any code that needs to change Gatsby's default HTML output. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do a relative link so gatsby-plugin-catch-links can do its thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also immediately make it clear this stuff is all involved when running gatsby build
. E.g. the second sentence is "This API is called when you run gatsby build
"
|
||
## Why does it cause build errors? | ||
|
||
When using `replaceRenderer` many times in your project, only the newest instance will be used - which can cause unexpected problems with your site. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/will/can/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"only the last plugin implementing the API can be called"
If your project uses replaceRenderer more than once, gatsby build will warn you: | ||
|
||
``` | ||
The "replaceRenderer" api has been implemented multiple times. Only the last implementation will be used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure the actor is mentioned i.e. plugin. So the sentence could read 'The "replaceRenderer" API is implemented by several enabled plugins."
The "replaceRenderer" api has been implemented multiple times. Only the last implementation will be used. | ||
This is probably an error, see https://example.com for workarounds. | ||
Check the following files for "replaceRenderer" implementations: | ||
/path/to/my/site/node_modules/gatsby-plugin-styled-components/gatsby-ssr.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd list the plugin name first (as that's what will be recognizable" and then list the exact path to the file implementing the API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the best way to get the plugin name? Just extract it from the path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could move the duplicate plugin detection to when we load the plugins. There we have access to all the info we need. Then when we write out the SSR runner, we just specify which plugin's implementation of replaceRenderer
it should run which is all that file needs to know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
In this example, your `gatsby-ssr.js` file and `gatsby-plugin-styled-components` are both using `replaceRenderer`. | ||
|
||
### 2. Move their `replaceRenderer` functionality to your `gatsby-ssr.js` file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Copy the plugins' replaceRenderer
functionality to your site's gatsby-ssr.js
file"
|
||
### Initial setup | ||
|
||
In this example project you're using [`redux`](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-redux) and [Gatsby's Styled Components plugin](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-styled-components). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Say "we" instead of "you"
@@ -6,10 +6,45 @@ | |||
|
|||
const apis = require(`./api-ssr-docs`) | |||
|
|||
module.exports = (api, args, defaultReturn) => { | |||
/** | |||
* Some apis should only be implemented once. Given a list of plugins, and an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Capitalize API
Yeah, that makes sense. The user probably has things handled at that point but it's still useful to warn people e.g. they add a custom |
Yes... that would be a bug... Hmmm not even sure when/where this is implemented. I think it's last one wins but that's kind of confusing as that means we're running code for API implementations we're not using. We should add the APIs here which can only be run once and then explicitly choose just the last one to run https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/cache-dir/api-runner-ssr.js |
d685018
to
e907671
Compare
That's great docs feedback, thanks! PR updated to cover that. Tomorrow I'll take another look at the functionality side of things. Note to self - update error message to mention plugin name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More copy tweaks
|
||
## What is the `replaceRenderer` API? | ||
|
||
The `replaceRenderer` API is one of [Gatsby's Server Side Rendering (SSR) hooks](/docs/ssr-apis/#replaceRenderer). This API is called when you run `gatsby build` and is used to customise how Gatsby renders your static content. It can be implemented by any Gatsby plugin or your `gatsby-ssr.js` file - adding support for Redux, CSS-in-JS libraries or any code that needs to change Gatsby's default HTML output. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be called "extension APIs" instead of hooks https://www.gatsbyjs.org/docs/api-specification/#extension-apis
|
||
## Why does it cause build errors? | ||
|
||
When using `replaceRenderer` many times in your project, only the last plugin implementing the API can be called - which will break your site builds. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"If multiple plugins implement replaceRenderer
in your project"
|
||
Note that `replaceRenderer` is only used during `gatsby build`. It won't cause problems as you work on your site with `gatsby develop`. | ||
|
||
If your project uses `replaceRenderer` more than once, `gatsby build` will warn you: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make similar change here. The project isn't the actor, plugins are.
e907671
to
4268d14
Compare
Deploy preview for using-glamor failed. Built with commit 4268d14 https://app.netlify.com/sites/using-glamor/deploys/5a7dbe6948769b76452b9f41 |
@KyleAMathews in A plugin that implements ssrAPIs won't have those APIs checked, its {
resolve: '/path/to/site/node_modules/gatsby-plugin-styled-components',
id: 'Plugin gatsby-plugin-styled-components',
name: 'gatsby-plugin-styled-components',
version: '2.0.5',
pluginOptions: { plugins: [] },
nodeAPIs: []
} should this object be expanded to include |
@m-allanson yeah I think so. I'm not entirely sure now why I didn't make it check browser/ssr APIs as well. |
Add link to https://github.com/caki0915/gatsby-starter-redux on the docs page? |
4268d14
to
0725c00
Compare
@KyleAMathews I was going to add some tests, but it's late so I'm just going to push this as-is. Here's what the various error outputs look like now: DevelopPossible error (build continues) There's three Definite error (build continues) There's multiple BuildPossible error (build continues) As with develop, show a warning but continue with the build. Definite error (build exits) As with develop, show an error but because this is definitely an error - stop the build. |
@@ -10,6 +10,7 @@ const nodeAPIs = require(`../utils/api-node-docs`) | |||
const browserAPIs = require(`../utils/api-browser-docs`) | |||
const ssrAPIs = require(`../../cache-dir/api-ssr-docs`) | |||
const resolveModuleExports = require(`./resolve-module-exports`) | |||
const reporter = require(`gatsby-cli/lib/reporter`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm maybe using reporter()
here isn't right?
- remove duplicate error messages - improve message format - only run the last replaceRenderer plugin
c8b6e47
to
19a1947
Compare
19a1947
to
a4807ee
Compare
@KyleAMathews The latest commit splits the plugin loader out into smaller pieces and adds some tests, so I think this is ready for another look. See my previous comment ⬆️for examples of the error messages. |
const hasAPIFile = (env, plugin) => { | ||
// The plugin loader has disabled SSR APIs for this plugin. Usually due to | ||
// multiple implementations of an API that can only be implemented once | ||
if (env === `ssr` && plugin.skipSSR === true) return undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we disable plugins for individual APIs not all SSR APIs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented disabling individual APIs on my checkout, but then had a thought - is there any reason for a plugin to implement replaceRenderer
and onRenderBody
? I can't find any official or community plugins that do this.
Disabling all SSR APIs keeps things simpler as it means api-runner-ssr.js
doesn't have to check whether it should run an API or not. I'd be tempted to roll with this implementation and then review if it causes problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does seem unlikely. If we add more APIs in the future, this could potentially become not true — that a plugin would implement multiple ssr APIs. I did a query (this one was fun ag -l replaceRenderer | xargs -L 1 ag onRenderBody -l
) and there isn't any plugin currently that implements both. So let's go with it and see what happens. It'd be easy to change later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds good. Nice querying :)
Great stuff! Super glad you took this on! It'll save a lot of people from long head-scratching debugging sessions! |
This has come up a whole bunch of times, see #2005, #2963, #3231, #2578, #1079 (there might be more).
Now when implementing
replaceRenderer
multiple times, you'll see a warning in the console.Still to do:
The warning is output multiple times, which is a bit messy, but an improvement on no output.Add a dedicated docs page demonstrating how to fix this. For now it points to @akadop's comment hereEnsure local gatsby-ssr.js takes precedence when replaceRenderer is implemented in multiple placesUse gatsby-cli's reporter for nicer output formatting?