Skip to content
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

Add browserAPIs and ssrAPIs to loaded plugin info #3989

Merged
merged 3 commits into from
Feb 12, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
exports[`Load plugins Loads plugins defined with an object but without an option key 1`] = `
Array [
Object {
"browserAPIs": Array [],
"id": "Plugin dev-404-page",
"name": "dev-404-page",
"nodeAPIs": Array [
Expand All @@ -12,9 +13,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin component-page-creator",
"name": "component-page-creator",
"nodeAPIs": Array [
Expand All @@ -24,9 +27,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin component-layout-creator",
"name": "component-layout-creator",
"nodeAPIs": Array [
Expand All @@ -36,9 +41,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin internal-data-bridge",
"name": "internal-data-bridge",
"nodeAPIs": Array [
Expand All @@ -49,9 +56,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin prod-404",
"name": "prod-404",
"nodeAPIs": Array [
Expand All @@ -61,9 +70,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin query-runner",
"name": "query-runner",
"nodeAPIs": Array [
Expand All @@ -74,24 +85,29 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"name": "TEST",
"nodeAPIs": Array [],
"pluginOptions": Object {
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
},
Object {
"browserAPIs": Array [],
"id": "Plugin default-site-plugin",
"name": "default-site-plugin",
"nodeAPIs": Array [],
"pluginOptions": Object {
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "d41d8cd98f00b204e9800998ecf8427e",
},
]
Expand All @@ -100,6 +116,7 @@ Array [
exports[`Load plugins load plugins for a site 1`] = `
Array [
Object {
"browserAPIs": Array [],
"id": "Plugin dev-404-page",
"name": "dev-404-page",
"nodeAPIs": Array [
Expand All @@ -109,9 +126,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin component-page-creator",
"name": "component-page-creator",
"nodeAPIs": Array [
Expand All @@ -121,9 +140,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin component-layout-creator",
"name": "component-layout-creator",
"nodeAPIs": Array [
Expand All @@ -133,9 +154,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin internal-data-bridge",
"name": "internal-data-bridge",
"nodeAPIs": Array [
Expand All @@ -146,9 +169,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin prod-404",
"name": "prod-404",
"nodeAPIs": Array [
Expand All @@ -158,9 +183,11 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin query-runner",
"name": "query-runner",
"nodeAPIs": Array [
Expand All @@ -171,16 +198,19 @@ Array [
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "1.0.0",
},
Object {
"browserAPIs": Array [],
"id": "Plugin default-site-plugin",
"name": "default-site-plugin",
"nodeAPIs": Array [],
"pluginOptions": Object {
"plugins": Array [],
},
"resolve": "",
"ssrAPIs": Array [],
"version": "d41d8cd98f00b204e9800998ecf8427e",
},
]
Expand Down
156 changes: 97 additions & 59 deletions packages/gatsby/src/bootstrap/load-plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,78 @@ const nodeAPIs = require(`../utils/api-node-docs`)
const testRequireError = require(`../utils/test-require-error`)
const report = require(`gatsby-cli/lib/reporter`)

// Given a plugin object and a moduleName like `gatsby-node`, check that the
// path to moduleName can be resolved.
const resolvePluginModule = (plugin, moduleName) => {
let resolved = false
try {
resolved = require(`${plugin.resolve}/${moduleName}`)
} catch (err) {
if (!testRequireError(moduleName, err)) {
// ignore
} else {
report.panic(`Error requiring ${plugin.resolve}/${moduleName}.js`, err)
}
}
return resolved
}

// Given a plugin object, an array of the API names it exports and an
// array of valid API names, return an array of invalid API exports.
const getBadExports = (plugin, pluginAPIKeys, apis) => {
let badExports = []
// Discover any exports from plugins which are not "known"
badExports = badExports.concat(
_.difference(pluginAPIKeys, apis).map(e => {
return {
exportName: e,
pluginName: plugin.name,
pluginVersion: plugin.version,
}
})
)
return badExports
}

const getBadExportsMessage = (badExports, apis) => {
const { stripIndent } = require(`common-tags`)
const stringSimiliarity = require(`string-similarity`)

let message = `\n`
message += stripIndent`
Your plugins must export known APIs from their gatsby-node.js.
The following exports aren't APIs. Perhaps you made a typo or
your plugin is outdated?

See https://www.gatsbyjs.org/docs/node-apis/ for the list of Gatsby Node APIs`

badExports.forEach(bady => {
const similarities = stringSimiliarity.findBestMatch(
bady.exportName,
apis
)
message += `\n — `
if (bady.pluginName == `default-site-plugin`) {
message += `Your site's gatsby-node.js is exporting a variable named "${
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message should vary now based on whether its node/ssr/browser apis right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, it should 👍

bady.exportName
}" which isn't an API.`
} else {
message += `The plugin "${bady.pluginName}@${
bady.pluginVersion
}" is exporting a variable named "${
bady.exportName
}" which isn't an API.`
}
if (similarities.bestMatch.rating > 0.5) {
message += ` Perhaps you meant to export "${
similarities.bestMatch.target
}"?`
}
})

return message
}

function createFileContentHash(root, globPattern) {
const hash = crypto.createHash(`md5`)
const files = glob.sync(`${root}/${globPattern}`, { nodir: true })
Expand Down Expand Up @@ -203,75 +275,41 @@ module.exports = async (config = {}) => {
}, {})
let badExports = []
flattenedPlugins.forEach(plugin => {
let gatsbyNode
plugin.nodeAPIs = []
try {
gatsbyNode = require(`${plugin.resolve}/gatsby-node`)
} catch (err) {
if (!testRequireError(`gatsby-node`, err)) {
// ignore
} else {
report.panic(`Error requiring ${plugin.resolve}/gatsby-node.js`, err)
}
}
plugin.browserAPIs = []
plugin.ssrAPIs = []

const gatsbyNode = resolvePluginModule(plugin, `gatsby-node`)
const gatsbyBrowser = resolvePluginModule(plugin, `gatsby-browser`)
const gatsbySSR = resolvePluginModule(plugin, `gatsby-ssr`)

// Discover which nodeAPIs this plugin implements and store
// an array against the plugin node itself *and* in a node
// API to plugins map for faster lookups later.
if (gatsbyNode) {
const gatsbyNodeKeys = _.keys(gatsbyNode)
// Discover which nodeAPIs this plugin implements and store
// an array against the plugin node itself *and* in a node
// API to plugins map for faster lookups later.
plugin.nodeAPIs = _.intersection(gatsbyNodeKeys, apis)
plugin.nodeAPIs.map(nodeAPI => apiToPlugins[nodeAPI].push(plugin.name))
// Discover any exports from plugins which are not "known"
badExports = badExports.concat(
_.difference(gatsbyNodeKeys, apis).map(e => {
return {
exportName: e,
pluginName: plugin.name,
pluginVersion: plugin.version,
}
})
)
badExports.concat(getBadExports(plugin, gatsbyNodeKeys, apis))
}

if (gatsbyBrowser) {
const gatsbyBrowserKeys = _.keys(gatsbyBrowser)
plugin.browserAPIs = _.intersection(gatsbyBrowserKeys, apis)
plugin.browserAPIs.map(browserAPI => apiToPlugins[browserAPI].push(plugin.name))
badExports.concat(getBadExports(plugin, gatsbyBrowserKeys, apis))
}

if (gatsbySSR) {
const gatsbySSRKeys = _.keys(gatsbySSR)
plugin.ssrAPIs = _.intersection(gatsbySSRKeys, apis)
plugin.ssrAPIs.map(ssrAPI => apiToPlugins[ssrAPI].push(plugin.name))
badExports.concat(getBadExports(plugin, gatsbySSRKeys, apis))
}
})

if (badExports.length > 0) {
const stringSimiliarity = require(`string-similarity`)
const { stripIndent } = require(`common-tags`)
console.log(`\n`)
console.log(
stripIndent`
Your plugins must export known APIs from their gatsby-node.js.
The following exports aren't APIs. Perhaps you made a typo or
your plugin is outdated?

See https://www.gatsbyjs.org/docs/node-apis/ for the list of Gatsby Node APIs`
)
badExports.forEach(bady => {
const similarities = stringSimiliarity.findBestMatch(
bady.exportName,
apis
)
let message = `\n — `
if (bady.pluginName == `default-site-plugin`) {
message += `Your site's gatsby-node.js is exporting a variable named "${
bady.exportName
}" which isn't an API.`
} else {
message += `The plugin "${bady.pluginName}@${
bady.pluginVersion
}" is exporting a variable named "${
bady.exportName
}" which isn't an API.`
}
if (similarities.bestMatch.rating > 0.5) {
message += ` Perhaps you meant to export "${
similarities.bestMatch.target
}"?`
}

console.log(message)
})
console.log(getBadExportsMessage(badExports, apis))
process.exit()
}

Expand Down