From 4259940f2b6d70ea7fb9f57d763dd6008caaceef Mon Sep 17 00:00:00 2001 From: Ward Peeters Date: Fri, 30 Apr 2021 02:01:23 +0200 Subject: [PATCH] feat(gatsby): enable gatsby-plugin-gatsby-cloud by default (#30624) * feat(gatsby): enable gatsby-plugin-gatsby-cloud by default when available * replace another constant * add tests --- packages/gatsby/package.json | 3 +- .../load-plugins/__tests__/load-plugins.ts | 219 ++++++++++++++---- .../src/bootstrap/load-plugins/index.ts | 2 +- .../gatsby/src/bootstrap/load-plugins/load.ts | 43 +++- 4 files changed, 207 insertions(+), 60 deletions(-) diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index a44a46396fe35..ce6e51e2163d7 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -118,6 +118,7 @@ "normalize-path": "^3.0.0", "null-loader": "^4.0.1", "opentracing": "^0.14.4", + "resolve-from": "^5.0.0", "p-defer": "^3.0.0", "parseurl": "^1.3.3", "path-to-regexp": "0.1.7", @@ -258,4 +259,4 @@ "yargs": { "boolean-negation": false } -} +} \ No newline at end of file diff --git a/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts b/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts index f3a858753dc53..f401bbaa9833b 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts +++ b/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts @@ -1,3 +1,9 @@ +import { loadPlugins } from "../index" +import { slash } from "gatsby-core-utils" +import reporter from "gatsby-cli/lib/reporter" +import { IFlattenedPlugin } from "../types" +import { silent as resolveFrom } from "resolve-from" + jest.mock(`gatsby-cli/lib/reporter`, () => { return { error: jest.fn(), @@ -8,16 +14,15 @@ jest.mock(`gatsby-cli/lib/reporter`, () => { info: jest.fn(), } }) + +jest.mock(`resolve-from`) const mockProcessExit = jest.spyOn(process, `exit`).mockImplementation(() => {}) -import { loadPlugins } from "../index" -import { slash } from "gatsby-core-utils" -import reporter from "gatsby-cli/lib/reporter" -import { IFlattenedPlugin } from "../types" afterEach(() => { Object.keys(reporter).forEach(method => { reporter[method].mockClear() }) + resolveFrom.mockClear() mockProcessExit.mockClear() }) @@ -51,7 +56,7 @@ describe(`Load plugins`, () => { }) it(`Load plugins for a site`, async () => { - let plugins = await loadPlugins({ plugins: [] }) + let plugins = await loadPlugins({ plugins: [] }, process.cwd()) plugins = replaceFieldsThatCanVary(plugins) @@ -67,7 +72,7 @@ describe(`Load plugins`, () => { ], } - let plugins = await loadPlugins(config) + let plugins = await loadPlugins(config, process.cwd()) plugins = replaceFieldsThatCanVary(plugins) @@ -88,7 +93,7 @@ describe(`Load plugins`, () => { } try { - await loadPlugins(config) + await loadPlugins(config, process.cwd()) } catch (err) { expect(err.message).toMatchSnapshot() } @@ -107,7 +112,7 @@ describe(`Load plugins`, () => { ], } - let plugins = await loadPlugins(config) + let plugins = await loadPlugins(config, process.cwd()) plugins = replaceFieldsThatCanVary(plugins) @@ -120,7 +125,7 @@ describe(`Load plugins`, () => { plugins: [], } - let plugins = await loadPlugins(config) + let plugins = await loadPlugins(config, process.cwd()) plugins = replaceFieldsThatCanVary(plugins) @@ -145,7 +150,7 @@ describe(`Load plugins`, () => { ], } - let plugins = await loadPlugins(config) + let plugins = await loadPlugins(config, process.cwd()) plugins = replaceFieldsThatCanVary(plugins) @@ -183,7 +188,7 @@ describe(`Load plugins`, () => { ], } - let plugins = await loadPlugins(config) + let plugins = await loadPlugins(config, process.cwd()) plugins = replaceFieldsThatCanVary(plugins) @@ -197,6 +202,105 @@ describe(`Load plugins`, () => { }) }) + describe(`Gatsby-plugin-gatsby-cloud support`, () => { + it(`doesn't gatsby-plugin-gatsby-cloud if not installed`, async () => { + resolveFrom.mockImplementation(() => undefined) + const config = { + plugins: [], + } + + let plugins = await loadPlugins(config, process.cwd()) + + plugins = replaceFieldsThatCanVary(plugins) + + expect(plugins).toEqual( + expect.arrayContaining([ + expect.not.objectContaining({ + name: `gatsby-plugin-gatsby-cloud`, + }), + ]) + ) + }) + + it(`loads gatsby-plugin-gatsby-cloud if not provided and installed`, async () => { + resolveFrom.mockImplementation( + (rootDir, pkg) => rootDir + `/node_modules/` + pkg + ) + const config = { + plugins: [], + } + + let plugins = await loadPlugins(config, process.cwd()) + + plugins = replaceFieldsThatCanVary(plugins) + + expect(plugins).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: `gatsby-plugin-gatsby-cloud`, + }), + ]) + ) + }) + + it(`uses the user provided plugin-gatsby-cloud if provided`, async () => { + resolveFrom.mockImplementation( + (rootDir, pkg) => rootDir + `/node_modules/` + pkg + ) + const config = { + plugins: [ + { + resolve: `gatsby-plugin-gatsby-cloud`, + options: { + generateMatchPathRewrites: false, + }, + }, + ], + } + + let plugins = await loadPlugins(config, process.cwd()) + + plugins = replaceFieldsThatCanVary(plugins) + + expect(plugins).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: `gatsby-plugin-gatsby-cloud`, + pluginOptions: { + generateMatchPathRewrites: false, + plugins: [], + }, + }), + ]) + ) + }) + + it(`does not add gatsby-plugin-gatsby-cloud if it exists in config.plugins`, async () => { + resolveFrom.mockImplementation( + (rootDir, pkg) => rootDir + `/node_modules/` + pkg + ) + const config = { + plugins: [ + `gatsby-plugin-gatsby-cloud`, + { resolve: `gatsby-plugin-gatsby-cloud` }, + ], + } + + let plugins = await loadPlugins(config, process.cwd()) + + plugins = replaceFieldsThatCanVary(plugins) + + const cloudPlugins = plugins.filter( + (plugin: { name: string }) => + plugin.name === `gatsby-plugin-gatsby-cloud` + ) + + // TODO: I think we should probably be de-duping, so this should be 1. + // But this test is mostly here to ensure we don't add an _additional_ gatsby-plugin-typescript + expect(cloudPlugins.length).toEqual(2) + }) + }) + describe(`plugin options validation`, () => { it(`throws a structured error with invalid plugin options`, async () => { const invalidPlugins = [ @@ -214,9 +318,12 @@ describe(`Load plugins`, () => { }, }, ] - await loadPlugins({ - plugins: invalidPlugins, - }) + await loadPlugins( + { + plugins: invalidPlugins, + }, + process.cwd() + ) expect(reporter.error as jest.Mock).toHaveBeenCalledTimes( invalidPlugins.length @@ -309,9 +416,12 @@ describe(`Load plugins`, () => { }, }, ] - await loadPlugins({ - plugins, - }) + await loadPlugins( + { + plugins, + }, + process.cwd() + ) expect(reporter.error as jest.Mock).toHaveBeenCalledTimes(0) expect(reporter.warn as jest.Mock).toHaveBeenCalledTimes(1) @@ -325,16 +435,19 @@ describe(`Load plugins`, () => { }) it(`defaults plugin options to the ones defined in the schema`, async () => { - let plugins = await loadPlugins({ - plugins: [ - { - resolve: `gatsby-plugin-google-analytics`, - options: { - trackingId: `fake`, + let plugins = await loadPlugins( + { + plugins: [ + { + resolve: `gatsby-plugin-google-analytics`, + options: { + trackingId: `fake`, + }, }, - }, - ], - }) + ], + }, + process.cwd() + ) plugins = replaceFieldsThatCanVary(plugins) @@ -354,23 +467,26 @@ describe(`Load plugins`, () => { }) it(`validates subplugin schemas`, async () => { - await loadPlugins({ - plugins: [ - { - resolve: `gatsby-transformer-remark`, - options: { - plugins: [ - { - resolve: `gatsby-remark-autolink-headers`, - options: { - maintainCase: `should be boolean`, + await loadPlugins( + { + plugins: [ + { + resolve: `gatsby-transformer-remark`, + options: { + plugins: [ + { + resolve: `gatsby-remark-autolink-headers`, + options: { + maintainCase: `should be boolean`, + }, }, - }, - ], + ], + }, }, - }, - ], - }) + ], + }, + process.cwd() + ) expect(reporter.error as jest.Mock).toHaveBeenCalledTimes(1) expect((reporter.error as jest.Mock).mock.calls[0]) @@ -403,16 +519,19 @@ describe(`Load plugins`, () => { }) it(`validates local plugin schemas using require.resolve`, async () => { - await loadPlugins({ - plugins: [ - { - resolve: require.resolve(`./fixtures/local-plugin`), - options: { - optionalString: 1234, + await loadPlugins( + { + plugins: [ + { + resolve: require.resolve(`./fixtures/local-plugin`), + options: { + optionalString: 1234, + }, }, - }, - ], - }) + ], + }, + process.cwd() + ) expect(reporter.error as jest.Mock).toHaveBeenCalledTimes(1) expect((reporter.error as jest.Mock).mock.calls[0]) diff --git a/packages/gatsby/src/bootstrap/load-plugins/index.ts b/packages/gatsby/src/bootstrap/load-plugins/index.ts index 7bc1d0b4200ec..bffe9d380d9ca 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/index.ts +++ b/packages/gatsby/src/bootstrap/load-plugins/index.ts @@ -82,7 +82,7 @@ const normalizeConfig = (config: IRawSiteConfig = {}): ISiteConfig => { export async function loadPlugins( rawConfig: IRawSiteConfig = {}, - rootDir: string | null = null + rootDir: string ): Promise> { // Turn all strings in plugins: [`...`] into the { resolve: ``, options: {} } form const config = normalizeConfig(rawConfig) diff --git a/packages/gatsby/src/bootstrap/load-plugins/load.ts b/packages/gatsby/src/bootstrap/load-plugins/load.ts index 57a0d181932f4..a8d5962ecbf93 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/load.ts +++ b/packages/gatsby/src/bootstrap/load-plugins/load.ts @@ -18,6 +18,10 @@ import { } from "./types" import { PackageJson } from "../../.." import reporter from "gatsby-cli/lib/reporter" +import { silent as resolveFromSilent } from "resolve-from" + +const GATSBY_CLOUD_PLUGIN_NAME = `gatsby-plugin-gatsby-cloud` +const TYPESCRIPT_PLUGIN_NAME = `gatsby-plugin-typescript` function createFileContentHash(root: string, globPattern: string): string { const hash = crypto.createHash(`md5`) @@ -141,12 +145,33 @@ export function resolvePlugin( } } +function addGatsbyPluginCloudPluginWhenInstalled( + plugins: Array, + processPlugin: (plugin: PluginRef) => IPluginInfo, + rootDir: string +): void { + const cloudPluginLocation = resolveFromSilent( + rootDir, + GATSBY_CLOUD_PLUGIN_NAME + ) + + if (cloudPluginLocation) { + plugins.push( + processPlugin({ + resolve: cloudPluginLocation, + options: {}, + }) + ) + } +} + export function loadPlugins( config: ISiteConfig = {}, - rootDir: string | null = null + rootDir: string ): Array { // Instantiate plugins. const plugins: Array = [] + const configuredPluginNames = new Set() // Create fake little site with a plugin for testing this // w/ snapshots. Move plugin processing to its own module. @@ -229,7 +254,9 @@ export function loadPlugins( // Add plugins from the site config. if (config.plugins) { config.plugins.forEach(plugin => { - plugins.push(processPlugin(plugin)) + const processedPlugin = processPlugin(plugin) + plugins.push(processedPlugin) + configuredPluginNames.add(processedPlugin.name) }) } @@ -250,15 +277,15 @@ export function loadPlugins( ) }) - // TypeScript support by default! use the user-provided one if it exists - const typescriptPlugin = (config.plugins || []).find( - plugin => plugin.resolve === `gatsby-plugin-typescript` - ) + if (!configuredPluginNames.has(GATSBY_CLOUD_PLUGIN_NAME)) { + addGatsbyPluginCloudPluginWhenInstalled(plugins, processPlugin, rootDir) + } - if (typescriptPlugin === undefined) { + // Suppor Typescript by default but allow users to override it + if (!configuredPluginNames.has(TYPESCRIPT_PLUGIN_NAME)) { plugins.push( processPlugin({ - resolve: require.resolve(`gatsby-plugin-typescript`), + resolve: require.resolve(TYPESCRIPT_PLUGIN_NAME), options: { // TODO(@mxstbr): Do not hard-code these defaults but infer them from the // pluginOptionsSchema of gatsby-plugin-typescript