From 1bc9609bada03e2e9764db3e94032e5b9ee0b1b1 Mon Sep 17 00:00:00 2001 From: Mike Allanson Date: Mon, 12 Feb 2018 13:02:38 +0000 Subject: [PATCH 1/3] Add browserAPIs and ssrAPIs to loaded plugin info --- .../__snapshots__/load-plugins.js.snap | 30 ++++ packages/gatsby/src/bootstrap/load-plugins.js | 156 +++++++++++------- 2 files changed, 127 insertions(+), 59 deletions(-) diff --git a/packages/gatsby/src/bootstrap/__tests__/__snapshots__/load-plugins.js.snap b/packages/gatsby/src/bootstrap/__tests__/__snapshots__/load-plugins.js.snap index 06aff2ffc9522..4609e91b310ea 100644 --- a/packages/gatsby/src/bootstrap/__tests__/__snapshots__/load-plugins.js.snap +++ b/packages/gatsby/src/bootstrap/__tests__/__snapshots__/load-plugins.js.snap @@ -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 [ @@ -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 [ @@ -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 [ @@ -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 [ @@ -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 [ @@ -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 [ @@ -74,17 +85,21 @@ 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 [], @@ -92,6 +107,7 @@ Array [ "plugins": Array [], }, "resolve": "", + "ssrAPIs": Array [], "version": "d41d8cd98f00b204e9800998ecf8427e", }, ] @@ -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 [ @@ -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 [ @@ -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 [ @@ -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 [ @@ -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 [ @@ -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 [ @@ -171,9 +198,11 @@ Array [ "plugins": Array [], }, "resolve": "", + "ssrAPIs": Array [], "version": "1.0.0", }, Object { + "browserAPIs": Array [], "id": "Plugin default-site-plugin", "name": "default-site-plugin", "nodeAPIs": Array [], @@ -181,6 +210,7 @@ Array [ "plugins": Array [], }, "resolve": "", + "ssrAPIs": Array [], "version": "d41d8cd98f00b204e9800998ecf8427e", }, ] diff --git a/packages/gatsby/src/bootstrap/load-plugins.js b/packages/gatsby/src/bootstrap/load-plugins.js index 47331eef13114..2b794aaafd2ef 100644 --- a/packages/gatsby/src/bootstrap/load-plugins.js +++ b/packages/gatsby/src/bootstrap/load-plugins.js @@ -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 "${ + 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 }) @@ -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() } From 5516c0e06653dafc23804aa652846d52757c973e Mon Sep 17 00:00:00 2001 From: Mike Allanson Date: Mon, 12 Feb 2018 21:18:31 +0000 Subject: [PATCH 2/3] Output correct error message for each bad export --- packages/gatsby/src/bootstrap/load-plugins.js | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/gatsby/src/bootstrap/load-plugins.js b/packages/gatsby/src/bootstrap/load-plugins.js index 2b794aaafd2ef..576c86f19aabc 100644 --- a/packages/gatsby/src/bootstrap/load-plugins.js +++ b/packages/gatsby/src/bootstrap/load-plugins.js @@ -15,7 +15,7 @@ const report = require(`gatsby-cli/lib/reporter`) const resolvePluginModule = (plugin, moduleName) => { let resolved = false try { - resolved = require(`${plugin.resolve}/${moduleName}`) + resolved = require(`${plugin.resolve}/${moduleName}`) } catch (err) { if (!testRequireError(moduleName, err)) { // ignore @@ -43,17 +43,19 @@ const getBadExports = (plugin, pluginAPIKeys, apis) => { return badExports } -const getBadExportsMessage = (badExports, apis) => { +const getBadExportsMessage = (badExports, exportType, apis) => { const { stripIndent } = require(`common-tags`) const stringSimiliarity = require(`string-similarity`) + let capitalized = `${exportType[0].toUpperCase()}${exportType.slice(1)}` + if (capitalized === `Ssr`) capitalized = `SSR` let message = `\n` message += stripIndent` - Your plugins must export known APIs from their gatsby-node.js. + Your plugins must export known APIs from their gatsby-${exportType}.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` + See https://www.gatsbyjs.org/docs/${exportType}-apis/ for the list of Gatsby ${capitalized} APIs` badExports.forEach(bady => { const similarities = stringSimiliarity.findBestMatch( @@ -62,7 +64,7 @@ const getBadExportsMessage = (badExports, apis) => { ) message += `\n — ` if (bady.pluginName == `default-site-plugin`) { - message += `Your site's gatsby-node.js is exporting a variable named "${ + message += `Your site's gatsby-${exportType}.js is exporting a variable named "${ bady.exportName }" which isn't an API.` } else { @@ -273,7 +275,14 @@ module.exports = async (config = {}) => { acc[value] = [] return acc }, {}) - let badExports = [] + + + const badExports = { + node: [], + browser: [], + ssr: [], + } + flattenedPlugins.forEach(plugin => { plugin.nodeAPIs = [] plugin.browserAPIs = [] @@ -283,35 +292,42 @@ module.exports = async (config = {}) => { 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. + // Discover which APIs this plugin implements and store an array against + // the plugin node itself *and* in an API to plugins map for faster lookups + // later. if (gatsbyNode) { const gatsbyNodeKeys = _.keys(gatsbyNode) plugin.nodeAPIs = _.intersection(gatsbyNodeKeys, apis) plugin.nodeAPIs.map(nodeAPI => apiToPlugins[nodeAPI].push(plugin.name)) - badExports.concat(getBadExports(plugin, gatsbyNodeKeys, apis)) + badExports.node = getBadExports(plugin, gatsbyNodeKeys, apis) // Collate any bad exports } 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)) + badExports.browser = getBadExports(plugin, gatsbyBrowserKeys, apis) // Collate any bad exports } 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)) + badExports.ssr = getBadExports(plugin, gatsbySSRKeys, apis) // Collate any bad exports } }) - if (badExports.length > 0) { - console.log(getBadExportsMessage(badExports, apis)) - process.exit() - } + // Output error messages for all bad exports + let bad = false + Object.entries(badExports).forEach(bad => { + const [exportType, entries] = bad + if (entries.length > 0) { + bad = true + console.log(getBadExportsMessage(entries, exportType, apis)) + } + }) + + if (bad) process.exit() store.dispatch({ type: `SET_SITE_PLUGINS`, From 066053f1cd589f2086909fa7000e5e9c75e65e61 Mon Sep 17 00:00:00 2001 From: Mike Allanson Date: Mon, 12 Feb 2018 21:57:34 +0000 Subject: [PATCH 3/3] Use lodash for Node 6 compatibility --- packages/gatsby/src/bootstrap/load-plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby/src/bootstrap/load-plugins.js b/packages/gatsby/src/bootstrap/load-plugins.js index 576c86f19aabc..0f44b55b9ac15 100644 --- a/packages/gatsby/src/bootstrap/load-plugins.js +++ b/packages/gatsby/src/bootstrap/load-plugins.js @@ -319,7 +319,7 @@ module.exports = async (config = {}) => { // Output error messages for all bad exports let bad = false - Object.entries(badExports).forEach(bad => { + _.toPairs(badExports).forEach(bad => { const [exportType, entries] = bad if (entries.length > 0) { bad = true