From a9c3eac0d211ee48c950a989b0ba181ab7f10bf8 Mon Sep 17 00:00:00 2001 From: pieh Date: Tue, 20 Dec 2022 23:53:07 +0100 Subject: [PATCH 1/9] fix(gatsby): handle initializing multiple instances of gatsby-plugin-sharp --- .../gatsby/src/utils/import-gatsby-plugin.ts | 4 +- .../gatsby/src/utils/merge-gatsby-config.ts | 52 ++++++++++++++----- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/gatsby/src/utils/import-gatsby-plugin.ts b/packages/gatsby/src/utils/import-gatsby-plugin.ts index 6c4ef347ad9c9..9e4a09ba2f5ba 100644 --- a/packages/gatsby/src/utils/import-gatsby-plugin.ts +++ b/packages/gatsby/src/utils/import-gatsby-plugin.ts @@ -11,7 +11,7 @@ export function setGatsbyPluginCache( module: string, moduleObject: any ): void { - const key = `${plugin.name}/${module}` + const key = `${plugin.resolve || plugin.name}/${module}` pluginModuleCache.set(key, moduleObject) } @@ -23,7 +23,7 @@ export async function importGatsbyPlugin( }, module: string ): Promise { - const key = `${plugin.name}/${module}` + const key = `${plugin.resolve || plugin.name}/${module}` let pluginModule = pluginModuleCache.get(key) diff --git a/packages/gatsby/src/utils/merge-gatsby-config.ts b/packages/gatsby/src/utils/merge-gatsby-config.ts index ba765a9d105b3..5f3bd2599352d 100644 --- a/packages/gatsby/src/utils/merge-gatsby-config.ts +++ b/packages/gatsby/src/utils/merge-gatsby-config.ts @@ -1,17 +1,23 @@ import _ from "lodash" import { Express } from "express" import type { TrailingSlash } from "gatsby-page-utils" +import mod from "module" // TODO export it in index.d.ts type PluginEntry = | string | { resolve: string options?: Record + parentDir: string } interface INormalizedPluginEntry { resolve: string options: Record + /** + * Absolute path to the plugin, used to dedupe different package instances + */ + _resolved: string } interface IGatsbyConfigInput { @@ -42,15 +48,33 @@ type Mapping = IGatsbyConfigInput["mapping"] * - { resolve: `gatsby-plugin-name`, options: {} } * are all considered equal */ -const normalizePluginEntry = (entry: PluginEntry): INormalizedPluginEntry => - _.isString(entry) - ? { - resolve: entry, - options: {}, - } - : _.isObject(entry) - ? { options: {}, ...entry } - : entry +const normalizePluginEntry = ( + entry: PluginEntry | INormalizedPluginEntry +): INormalizedPluginEntry => { + if (_.isString(entry)) { + return { + resolve: entry, + options: {}, + _resolved: require.resolve(entry), + } + } else if (_.isObject(entry)) { + let _resolved: string + if (`parentDir` in entry) { + const localRequire = mod.createRequire(entry.parentDir + `/:internal:`) + _resolved = localRequire.resolve(entry.resolve) + } else { + _resolved = entry._resolved + } + + return { + options: {}, + ...entry, + _resolved, + } + } else { + return entry + } +} const howToMerge = { /** @@ -63,13 +87,13 @@ const howToMerge = { _.merge({}, objA, objB), // plugins are concatenated and uniq'd, so we don't get two of the same plugin value plugins: ( - a: Array = [], - b: Array = [] - ): Array => + a: Array = [], + b: Array = [] + ): Array => _.uniqWith(a.concat(b), (a, b) => _.isEqual( - _.pick(normalizePluginEntry(a), [`resolve`, `options`]), - _.pick(normalizePluginEntry(b), [`resolve`, `options`]) + _.pick(normalizePluginEntry(a), [`resolve`, `options`, `_resolved`]), + _.pick(normalizePluginEntry(b), [`resolve`, `options`, `_resolved`]) ) ), mapping: (objA: Mapping, objB: Mapping): Mapping => _.merge({}, objA, objB), From ad6a1e92f4dac6bfe97f048eea4ff5ed343d86b9 Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 00:15:57 +0100 Subject: [PATCH 2/9] fix(gatsby): handle initializing multiple instances of gatsby-plugin-sharp in engines --- .../gatsby/src/schema/graphql-engine/entry.ts | 14 ++++++---- .../schema/graphql-engine/print-plugins.ts | 28 +++++++++++-------- .../gatsby/src/utils/import-gatsby-plugin.ts | 13 +++++++-- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/packages/gatsby/src/schema/graphql-engine/entry.ts b/packages/gatsby/src/schema/graphql-engine/entry.ts index c7383fbf0d0b6..f64a93df57561 100644 --- a/packages/gatsby/src/schema/graphql-engine/entry.ts +++ b/packages/gatsby/src/schema/graphql-engine/entry.ts @@ -59,18 +59,20 @@ export class GraphQLEngine { payload: flattenedPlugins, }) - for (const pluginName of Object.keys(gatsbyNodes)) { + for (const plugin of gatsbyNodes) { + const { name, module, importKey } = plugin setGatsbyPluginCache( - { name: pluginName, resolve: `` }, + { name, resolve: ``, importKey }, `gatsby-node`, - gatsbyNodes[pluginName] + module ) } - for (const pluginName of Object.keys(gatsbyWorkers)) { + for (const plugin of gatsbyWorkers) { + const { name, module, importKey } = plugin setGatsbyPluginCache( - { name: pluginName, resolve: `` }, + { name, resolve: ``, importKey }, `gatsby-worker`, - gatsbyWorkers[pluginName] + module ) } diff --git a/packages/gatsby/src/schema/graphql-engine/print-plugins.ts b/packages/gatsby/src/schema/graphql-engine/print-plugins.ts index 2019e93184f6f..568837aeca615 100644 --- a/packages/gatsby/src/schema/graphql-engine/print-plugins.ts +++ b/packages/gatsby/src/schema/graphql-engine/print-plugins.ts @@ -53,24 +53,24 @@ async function render( usedPlugins: IGatsbyState["flattenedPlugins"], usedSubPlugins: IGatsbyState["flattenedPlugins"] ): Promise { - const uniqGatsbyNode = uniq(usedPlugins) const uniqSubPlugins = uniq(usedSubPlugins) - const sanitizedUsedPlugins = usedPlugins.map(plugin => { + const sanitizedUsedPlugins = usedPlugins.map((plugin, i) => { // TODO: We don't support functions in pluginOptions here return { ...plugin, resolve: ``, pluginFilepath: ``, subPluginPaths: undefined, + importKey: i + 1, } }) - const pluginsWithWorkers = await filterPluginsWithWorkers(uniqGatsbyNode) + const pluginsWithWorkers = await filterPluginsWithWorkers(usedPlugins) const subPluginModuleToImportNameMapping = new Map() const imports: Array = [ - ...uniqGatsbyNode.map( + ...usedPlugins.map( (plugin, i) => `import * as pluginGatsbyNode${i} from "${relativePluginPath( plugin.resolve @@ -90,22 +90,28 @@ async function render( )}"` }), ] - const gatsbyNodeExports = uniqGatsbyNode.map( - (plugin, i) => `"${plugin.name}": pluginGatsbyNode${i},` + const gatsbyNodeExports = usedPlugins.map( + (plugin, i) => + `{ name: "${plugin.name}", module: pluginGatsbyNode${i}, importKey: ${ + i + 1 + } },` ) const gatsbyWorkerExports = pluginsWithWorkers.map( - (plugin, i) => `"${plugin.name}": pluginGatsbyWorker${i},` + (plugin, i) => + `{ name: "${plugin.name}", module: pluginGatsbyWorker${i}, importKey: ${ + i + 1 + } },` ) const output = ` ${imports.join(`\n`)} -export const gatsbyNodes = { +export const gatsbyNodes = [ ${gatsbyNodeExports.join(`\n`)} -} +] -export const gatsbyWorkers = { +export const gatsbyWorkers = [ ${gatsbyWorkerExports.join(`\n`)} -} +] export const flattenedPlugins = ${JSON.stringify( diff --git a/packages/gatsby/src/utils/import-gatsby-plugin.ts b/packages/gatsby/src/utils/import-gatsby-plugin.ts index 9e4a09ba2f5ba..fc5ef978a4c86 100644 --- a/packages/gatsby/src/utils/import-gatsby-plugin.ts +++ b/packages/gatsby/src/utils/import-gatsby-plugin.ts @@ -7,12 +7,18 @@ import { preferDefault } from "../bootstrap/prefer-default" const pluginModuleCache = new Map() export function setGatsbyPluginCache( - plugin: { name: string; resolve: string }, + plugin: { name: string; resolve: string; importKey?: string }, module: string, moduleObject: any ): void { - const key = `${plugin.resolve || plugin.name}/${module}` + const key = `${plugin.name}/${module}` pluginModuleCache.set(key, moduleObject) + + const additionalPrefix = plugin.importKey || plugin.resolve + if (additionalPrefix) { + const key = `${additionalPrefix}/${module}` + pluginModuleCache.set(key, moduleObject) + } } export async function importGatsbyPlugin( @@ -20,10 +26,11 @@ export async function importGatsbyPlugin( name: string resolve: string resolvedCompiledGatsbyNode?: string + importKey?: string }, module: string ): Promise { - const key = `${plugin.resolve || plugin.name}/${module}` + const key = `${plugin.importKey || plugin.resolve || plugin.name}/${module}` let pluginModule = pluginModuleCache.get(key) From a17e5f33319dac7a718257cc98521105ec87c76e Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 00:16:35 +0100 Subject: [PATCH 3/9] update standalone-regenerate --- .../gatsby/src/schema/graphql-engine/standalone-regenerate.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gatsby/src/schema/graphql-engine/standalone-regenerate.ts b/packages/gatsby/src/schema/graphql-engine/standalone-regenerate.ts index 628725bf98508..a76ef7bc14a96 100644 --- a/packages/gatsby/src/schema/graphql-engine/standalone-regenerate.ts +++ b/packages/gatsby/src/schema/graphql-engine/standalone-regenerate.ts @@ -26,6 +26,7 @@ import { store } from "../../redux" import { validateEngines } from "../../utils/validate-engines" async function run(): Promise { + process.env.GATSBY_SLICES = `1` // load config console.log(`loading config and plugins`) await loadConfigAndPlugins({ From 67dc92c98352903b03e182352916fd2acbefd798 Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 11:20:51 +0100 Subject: [PATCH 4/9] normalize main config when loading themes --- packages/gatsby/src/bootstrap/load-themes/index.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/gatsby/src/bootstrap/load-themes/index.js b/packages/gatsby/src/bootstrap/load-themes/index.js index d38dfef3c1895..e9aab47f26279 100644 --- a/packages/gatsby/src/bootstrap/load-themes/index.js +++ b/packages/gatsby/src/bootstrap/load-themes/index.js @@ -157,7 +157,18 @@ module.exports = async (config, { configFilePath, rootDir }) => { .reduce(mergeGatsbyConfig, {}) .then(newConfig => { return { - config: mergeGatsbyConfig(newConfig, config), + config: mergeGatsbyConfig(newConfig, { + ...config, + plugins: [ + ...(config.plugins || []).map(plugin => { + return { + resolve: typeof plugin === `string` ? plugin : plugin.resolve, + options: plugin.options || {}, + parentDir: rootDir, + } + }), + ], + }), themes: themesA, } }) From 521b7dcb5e6eeb5b2261f053a66f67591f16d063 Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 18:00:51 +0100 Subject: [PATCH 5/9] move deduplicaiton to plugin loading instead of themes loading --- .../load-plugins/load-internal-plugins.ts | 5 +- .../gatsby/src/bootstrap/load-themes/index.js | 28 ++++----- .../gatsby/src/utils/merge-gatsby-config.ts | 58 ++----------------- 3 files changed, 22 insertions(+), 69 deletions(-) diff --git a/packages/gatsby/src/bootstrap/load-plugins/load-internal-plugins.ts b/packages/gatsby/src/bootstrap/load-plugins/load-internal-plugins.ts index e04783ee97f9b..e58a38e1deb64 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/load-internal-plugins.ts +++ b/packages/gatsby/src/bootstrap/load-plugins/load-internal-plugins.ts @@ -1,4 +1,5 @@ import { slash } from "gatsby-core-utils" +import { uniqWith, isEqual } from "lodash" import path from "path" import reporter from "gatsby-cli/lib/reporter" import { store } from "../../redux" @@ -170,5 +171,7 @@ export function loadInternalPlugins( ) ) - return plugins + const uniquePlugins = uniqWith(plugins, isEqual) + + return uniquePlugins } diff --git a/packages/gatsby/src/bootstrap/load-themes/index.js b/packages/gatsby/src/bootstrap/load-themes/index.js index e9aab47f26279..c326fd7d43c4e 100644 --- a/packages/gatsby/src/bootstrap/load-themes/index.js +++ b/packages/gatsby/src/bootstrap/load-themes/index.js @@ -108,6 +108,14 @@ const processTheme = ( } } +function normalizePluginEntry(plugin, parentDir) { + return { + resolve: typeof plugin === `string` ? plugin : plugin.resolve, + options: plugin.options || {}, + parentDir, + } +} + module.exports = async (config, { configFilePath, rootDir }) => { const themesA = await Promise.mapSeries( config.plugins || [], @@ -135,13 +143,9 @@ module.exports = async (config, { configFilePath, rootDir }) => { return { ...themeConfig, plugins: [ - ...(themeConfig.plugins || []).map(plugin => { - return { - resolve: typeof plugin === `string` ? plugin : plugin.resolve, - options: plugin.options || {}, - parentDir: themeDir, - } - }), + ...(themeConfig.plugins || []).map(plugin => + normalizePluginEntry(plugin, themeDir) + ), // theme plugin is last so it's gatsby-node, etc can override it's declared plugins, like a normal site. { resolve: themeName, options: themeSpec.options || {}, parentDir }, ], @@ -160,13 +164,9 @@ module.exports = async (config, { configFilePath, rootDir }) => { config: mergeGatsbyConfig(newConfig, { ...config, plugins: [ - ...(config.plugins || []).map(plugin => { - return { - resolve: typeof plugin === `string` ? plugin : plugin.resolve, - options: plugin.options || {}, - parentDir: rootDir, - } - }), + ...(config.plugins || []).map(plugin => + normalizePluginEntry(plugin, rootDir) + ), ], }), themes: themesA, diff --git a/packages/gatsby/src/utils/merge-gatsby-config.ts b/packages/gatsby/src/utils/merge-gatsby-config.ts index 5f3bd2599352d..031f3f4cc26c0 100644 --- a/packages/gatsby/src/utils/merge-gatsby-config.ts +++ b/packages/gatsby/src/utils/merge-gatsby-config.ts @@ -1,7 +1,7 @@ import _ from "lodash" import { Express } from "express" import type { TrailingSlash } from "gatsby-page-utils" -import mod from "module" + // TODO export it in index.d.ts type PluginEntry = | string @@ -11,15 +11,6 @@ type PluginEntry = parentDir: string } -interface INormalizedPluginEntry { - resolve: string - options: Record - /** - * Absolute path to the plugin, used to dedupe different package instances - */ - _resolved: string -} - interface IGatsbyConfigInput { siteMetadata?: Record plugins?: Array @@ -41,41 +32,6 @@ type ConfigKey = keyof IGatsbyConfigInput type Metadata = IGatsbyConfigInput["siteMetadata"] type Mapping = IGatsbyConfigInput["mapping"] -/** - * Normalize plugin spec before comparing so - * - `gatsby-plugin-name` - * - { resolve: `gatsby-plugin-name` } - * - { resolve: `gatsby-plugin-name`, options: {} } - * are all considered equal - */ -const normalizePluginEntry = ( - entry: PluginEntry | INormalizedPluginEntry -): INormalizedPluginEntry => { - if (_.isString(entry)) { - return { - resolve: entry, - options: {}, - _resolved: require.resolve(entry), - } - } else if (_.isObject(entry)) { - let _resolved: string - if (`parentDir` in entry) { - const localRequire = mod.createRequire(entry.parentDir + `/:internal:`) - _resolved = localRequire.resolve(entry.resolve) - } else { - _resolved = entry._resolved - } - - return { - options: {}, - ...entry, - _resolved, - } - } else { - return entry - } -} - const howToMerge = { /** * pick a truthy value by default. @@ -87,15 +43,9 @@ const howToMerge = { _.merge({}, objA, objB), // plugins are concatenated and uniq'd, so we don't get two of the same plugin value plugins: ( - a: Array = [], - b: Array = [] - ): Array => - _.uniqWith(a.concat(b), (a, b) => - _.isEqual( - _.pick(normalizePluginEntry(a), [`resolve`, `options`, `_resolved`]), - _.pick(normalizePluginEntry(b), [`resolve`, `options`, `_resolved`]) - ) - ), + a: Array = [], + b: Array = [] + ): Array => a.concat(b), mapping: (objA: Mapping, objB: Mapping): Mapping => _.merge({}, objA, objB), } as const From 30ab3df4df268c49842f27394d9531b9a1dbc166 Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 18:52:50 +0100 Subject: [PATCH 6/9] convert load-themes to TS --- .../gatsby/src/bootstrap/load-config/index.ts | 2 +- .../bootstrap/load-themes/__tests__/index.js | 2 +- .../load-themes/{index.js => index.ts} | 139 ++++++++++++------ .../gatsby/src/utils/merge-gatsby-config.ts | 15 +- 4 files changed, 103 insertions(+), 55 deletions(-) rename packages/gatsby/src/bootstrap/load-themes/{index.js => index.ts} (60%) diff --git a/packages/gatsby/src/bootstrap/load-config/index.ts b/packages/gatsby/src/bootstrap/load-config/index.ts index 8d4e98ef2a0f4..585dfe8bc4a1f 100644 --- a/packages/gatsby/src/bootstrap/load-config/index.ts +++ b/packages/gatsby/src/bootstrap/load-config/index.ts @@ -3,7 +3,7 @@ import telemetry from "gatsby-telemetry" import { preferDefault } from "../prefer-default" import { getConfigFile } from "../get-config-file" import { internalActions } from "../../redux/actions" -import loadThemes from "../load-themes" +import { loadThemes } from "../load-themes" import { store } from "../../redux" import handleFlags from "../../utils/handle-flags" import availableFlags from "../../utils/flags" diff --git a/packages/gatsby/src/bootstrap/load-themes/__tests__/index.js b/packages/gatsby/src/bootstrap/load-themes/__tests__/index.js index 68380baad20f8..026303262fade 100644 --- a/packages/gatsby/src/bootstrap/load-themes/__tests__/index.js +++ b/packages/gatsby/src/bootstrap/load-themes/__tests__/index.js @@ -1,4 +1,4 @@ -const loadThemes = require(`..`) +const { loadThemes } = require(`..`) const path = require(`path`) describe(`loadThemes`, () => { diff --git a/packages/gatsby/src/bootstrap/load-themes/index.js b/packages/gatsby/src/bootstrap/load-themes/index.ts similarity index 60% rename from packages/gatsby/src/bootstrap/load-themes/index.js rename to packages/gatsby/src/bootstrap/load-themes/index.ts index c326fd7d43c4e..c853796e93b15 100644 --- a/packages/gatsby/src/bootstrap/load-themes/index.js +++ b/packages/gatsby/src/bootstrap/load-themes/index.ts @@ -1,22 +1,39 @@ -const { createRequireFromPath } = require(`gatsby-core-utils`) -const path = require(`path`) -import { mergeGatsbyConfig } from "../../utils/merge-gatsby-config" -const Promise = require(`bluebird`) -const _ = require(`lodash`) -const debug = require(`debug`)(`gatsby:load-themes`) +import { createRequireFromPath } from "gatsby-core-utils" +import * as path from "path" +import { + IGatsbyConfigInput, + mergeGatsbyConfig, + PluginEntry, + IPluginEntryWithParentDir, +} from "../../utils/merge-gatsby-config" +import { mapSeries } from "bluebird" +import { flattenDeep, isEqual, isFunction, uniqWith } from "lodash" +import DebugCtor from "debug" import { preferDefault } from "../prefer-default" import { getConfigFile } from "../get-config-file" import { resolvePlugin } from "../load-plugins/resolve-plugin" -const reporter = require(`gatsby-cli/lib/reporter`) +import reporter from "gatsby-cli/lib/reporter" + +const debug = DebugCtor(`gatsby:load-themes`) + +interface IThemeObj { + themeName: string + themeConfig: IGatsbyConfigInput + themeDir: string + themeSpec: PluginEntry + parentDir: string + configFilePath?: string +} // get the gatsby-config file for a theme const resolveTheme = async ( - themeSpec, - configFileThatDeclaredTheme, - isMainConfig = false, - rootDir -) => { - const themeName = themeSpec.resolve || themeSpec + themeSpec: PluginEntry, + configFileThatDeclaredTheme: string | undefined, + isMainConfig: boolean = false, + rootDir: string +): Promise => { + const themeName = + typeof themeSpec === `string` ? themeSpec : themeSpec.resolve let themeDir try { const scopedRequire = createRequireFromPath(`${rootDir}/:internal:`) @@ -59,13 +76,16 @@ const resolveTheme = async ( themeDir, `gatsby-config` ) - const theme = preferDefault(configModule) + const theme: + | IGatsbyConfigInput + | ((options?: Record) => IGatsbyConfigInput) = + preferDefault(configModule) // if theme is a function, call it with the themeConfig - let themeConfig = theme - if (_.isFunction(theme)) { - themeConfig = theme(themeSpec.options || {}) - } + const themeConfig = isFunction(theme) + ? theme(typeof themeSpec === `string` ? {} : themeSpec.options) + : theme + return { themeName, themeConfig, @@ -84,9 +104,9 @@ const resolveTheme = async ( // no use case for a loop so I expect that to only happen if someone is very // off track and creating their own set of themes const processTheme = ( - { themeName, themeConfig, themeSpec, themeDir, configFilePath }, - { rootDir } -) => { + { themeName, themeConfig, themeSpec, themeDir, configFilePath }: IThemeObj, + { rootDir }: { rootDir: string } +): Promise> => { const themesList = themeConfig && themeConfig.plugins // Gatsby themes don't have to specify a gatsby-config.js (they might only use gatsby-node, etc) // in this case they're technically plugins, but we should support it anyway @@ -94,32 +114,53 @@ const processTheme = ( if (themeConfig && themesList) { // for every parent theme a theme defines, resolve the parent's // gatsby config and return it in order [parentA, parentB, child] - return Promise.mapSeries(themesList, async spec => { - const themeObj = await resolveTheme(spec, configFilePath, false, themeDir) - return processTheme(themeObj, { rootDir: themeDir }) - }).then(arr => - arr.concat([ - { themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir }, - ]) + return mapSeries( + themesList, + async (spec: PluginEntry): Promise> => { + const themeObj = await resolveTheme( + spec, + configFilePath, + false, + themeDir + ) + return processTheme(themeObj, { rootDir: themeDir }) + } + ).then(arr => + flattenDeep( + arr.concat([ + { themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir }, + ]) + ) ) } else { // if a theme doesn't define additional themes, return the original theme - return [{ themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir }] + return Promise.resolve([ + { themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir }, + ]) } } -function normalizePluginEntry(plugin, parentDir) { +function normalizePluginEntry( + plugin: PluginEntry, + parentDir: string +): IPluginEntryWithParentDir { return { resolve: typeof plugin === `string` ? plugin : plugin.resolve, - options: plugin.options || {}, + options: typeof plugin === `string` ? {} : plugin.options || {}, parentDir, } } -module.exports = async (config, { configFilePath, rootDir }) => { - const themesA = await Promise.mapSeries( +export async function loadThemes( + config: IGatsbyConfigInput, + { configFilePath, rootDir }: { configFilePath: string; rootDir: string } +): Promise<{ + config: IGatsbyConfigInput + themes: Array +}> { + const themesA = await mapSeries( config.plugins || [], - async themeSpec => { + async (themeSpec: PluginEntry) => { const themeObj = await resolveTheme( themeSpec, configFilePath, @@ -128,7 +169,7 @@ module.exports = async (config, { configFilePath, rootDir }) => { ) return processTheme(themeObj, { rootDir }) } - ).then(arr => _.flattenDeep(arr)) + ).then(arr => flattenDeep(arr)) // log out flattened themes list to aid in debugging debug(themesA) @@ -137,7 +178,7 @@ module.exports = async (config, { configFilePath, rootDir }) => { // list in the config for the theme. This enables the usage of // gatsby-node, etc in themes. return ( - Promise.mapSeries( + mapSeries( themesA, ({ themeName, themeConfig = {}, themeSpec, themeDir, parentDir }) => { return { @@ -147,7 +188,11 @@ module.exports = async (config, { configFilePath, rootDir }) => { normalizePluginEntry(plugin, themeDir) ), // theme plugin is last so it's gatsby-node, etc can override it's declared plugins, like a normal site. - { resolve: themeName, options: themeSpec.options || {}, parentDir }, + { + resolve: themeName, + options: typeof themeSpec === `string` ? {} : themeSpec.options, + parentDir, + }, ], } } @@ -160,15 +205,19 @@ module.exports = async (config, { configFilePath, rootDir }) => { */ .reduce(mergeGatsbyConfig, {}) .then(newConfig => { + const mergedConfig = mergeGatsbyConfig(newConfig, { + ...config, + plugins: [ + ...(config.plugins || []).map(plugin => + normalizePluginEntry(plugin, rootDir) + ), + ], + }) + + mergedConfig.plugins = uniqWith(mergedConfig.plugins, isEqual) + return { - config: mergeGatsbyConfig(newConfig, { - ...config, - plugins: [ - ...(config.plugins || []).map(plugin => - normalizePluginEntry(plugin, rootDir) - ), - ], - }), + config: mergedConfig, themes: themesA, } }) diff --git a/packages/gatsby/src/utils/merge-gatsby-config.ts b/packages/gatsby/src/utils/merge-gatsby-config.ts index 031f3f4cc26c0..31dad36d446db 100644 --- a/packages/gatsby/src/utils/merge-gatsby-config.ts +++ b/packages/gatsby/src/utils/merge-gatsby-config.ts @@ -2,16 +2,15 @@ import _ from "lodash" import { Express } from "express" import type { TrailingSlash } from "gatsby-page-utils" +export type IPluginEntryWithParentDir = { + resolve: string + options?: Record + parentDir: string +} // TODO export it in index.d.ts -type PluginEntry = - | string - | { - resolve: string - options?: Record - parentDir: string - } +export type PluginEntry = string | IPluginEntryWithParentDir -interface IGatsbyConfigInput { +export interface IGatsbyConfigInput { siteMetadata?: Record plugins?: Array pathPrefix?: string From 0fd07254b5e0851a089d6c838c02f5d307203ce2 Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 19:05:54 +0100 Subject: [PATCH 7/9] lint --- packages/gatsby/src/utils/merge-gatsby-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby/src/utils/merge-gatsby-config.ts b/packages/gatsby/src/utils/merge-gatsby-config.ts index 31dad36d446db..cd755e45c26ea 100644 --- a/packages/gatsby/src/utils/merge-gatsby-config.ts +++ b/packages/gatsby/src/utils/merge-gatsby-config.ts @@ -2,7 +2,7 @@ import _ from "lodash" import { Express } from "express" import type { TrailingSlash } from "gatsby-page-utils" -export type IPluginEntryWithParentDir = { +export interface IPluginEntryWithParentDir { resolve: string options?: Record parentDir: string From 593dc1db421f0f904e4764b993fad71fa79ffd4c Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 19:25:05 +0100 Subject: [PATCH 8/9] update assertions --- .../src/bootstrap/load-plugins/__tests__/load-plugins.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 c3e1afafed364..535b8bcccfb7d 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts +++ b/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts @@ -218,9 +218,7 @@ describe(`Load plugins`, () => { (plugin: { name: string }) => plugin.name === `gatsby-plugin-typescript` ) - // 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(tsplugins.length).toEqual(2) + expect(tsplugins.length).toEqual(1) }) }) @@ -351,9 +349,7 @@ describe(`Load plugins`, () => { 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) + expect(cloudPlugins.length).toEqual(1) }) }) From 5b86e2d024c8a91703d8918d3f607cde46e718d4 Mon Sep 17 00:00:00 2001 From: pieh Date: Wed, 21 Dec 2022 20:12:44 +0100 Subject: [PATCH 9/9] remove test that no longer make sense --- .../utils/__tests__/merge-gatsby-config.ts | 61 ------------------- 1 file changed, 61 deletions(-) diff --git a/packages/gatsby/src/utils/__tests__/merge-gatsby-config.ts b/packages/gatsby/src/utils/__tests__/merge-gatsby-config.ts index 9fe5959f5b17d..f16f217c35638 100644 --- a/packages/gatsby/src/utils/__tests__/merge-gatsby-config.ts +++ b/packages/gatsby/src/utils/__tests__/merge-gatsby-config.ts @@ -36,67 +36,6 @@ describe(`Merge gatsby config`, () => { }) }) - it(`Merging plugins uniqs them, keeping the first occurrence`, () => { - const basicConfig = { - plugins: [ - `gatsby-plugin-mdx`, - { - resolve: `scoped-plugin`, - options: {}, - parentDir: `/path/to/scoped-basic/parent`, - }, - ], - } - const morePlugins = { - plugins: [ - `a-plugin`, - `gatsby-plugin-mdx`, - `b-plugin`, - { - resolve: `c-plugin`, - options: {}, - }, - { - resolve: `scoped-plugin`, - options: {}, - parentDir: `/path/to/scoped-more/parent`, - }, - ], - } - expect(mergeGatsbyConfig(basicConfig, morePlugins)).toEqual({ - plugins: [ - `gatsby-plugin-mdx`, - { - resolve: `scoped-plugin`, - options: {}, - parentDir: `/path/to/scoped-basic/parent`, - }, - `a-plugin`, - `b-plugin`, - { - resolve: `c-plugin`, - options: {}, - }, - ], - }) - expect(mergeGatsbyConfig(morePlugins, basicConfig)).toEqual({ - plugins: [ - `a-plugin`, - `gatsby-plugin-mdx`, - `b-plugin`, - { - resolve: `c-plugin`, - options: {}, - }, - { - resolve: `scoped-plugin`, - options: {}, - parentDir: `/path/to/scoped-more/parent`, - }, - ], - }) - }) - it(`Merging siteMetadata is recursive`, () => { const a = { siteMetadata: {