Skip to content

Commit 185f951

Browse files
committed
fix(gatsby): handle initializing multiple instances of gatsby-plugin-sharp (#37306)
* fix(gatsby): handle initializing multiple instances of gatsby-plugin-sharp * fix(gatsby): handle initializing multiple instances of gatsby-plugin-sharp in engines * update standalone-regenerate * normalize main config when loading themes * move deduplicaiton to plugin loading instead of themes loading * convert load-themes to TS * lint * update assertions * remove test that no longer make sense (cherry picked from commit 26f2b72)
1 parent 4dcca80 commit 185f951

File tree

11 files changed

+152
-165
lines changed

11 files changed

+152
-165
lines changed

packages/gatsby/src/bootstrap/load-config/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import telemetry from "gatsby-telemetry"
33
import { preferDefault } from "../prefer-default"
44
import { getConfigFile } from "../get-config-file"
55
import { internalActions } from "../../redux/actions"
6-
import loadThemes from "../load-themes"
6+
import { loadThemes } from "../load-themes"
77
import { store } from "../../redux"
88
import handleFlags from "../../utils/handle-flags"
99
import availableFlags from "../../utils/flags"

packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,7 @@ describe(`Load plugins`, () => {
197197
(plugin: { name: string }) => plugin.name === `gatsby-plugin-typescript`
198198
)
199199

200-
// TODO: I think we should probably be de-duping, so this should be 1.
201-
// But this test is mostly here to ensure we don't add an _additional_ gatsby-plugin-typescript
202-
expect(tsplugins.length).toEqual(2)
200+
expect(tsplugins.length).toEqual(1)
203201
})
204202
})
205203

@@ -330,9 +328,7 @@ describe(`Load plugins`, () => {
330328
plugin.name === `gatsby-plugin-gatsby-cloud`
331329
)
332330

333-
// TODO: I think we should probably be de-duping, so this should be 1.
334-
// But this test is mostly here to ensure we don't add an _additional_ gatsby-plugin-typescript
335-
expect(cloudPlugins.length).toEqual(2)
331+
expect(cloudPlugins.length).toEqual(1)
336332
})
337333
})
338334

packages/gatsby/src/bootstrap/load-plugins/load-internal-plugins.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { slash } from "gatsby-core-utils"
2+
import { uniqWith, isEqual } from "lodash"
23
import path from "path"
34
import reporter from "gatsby-cli/lib/reporter"
45
import { store } from "../../redux"
@@ -170,5 +171,7 @@ export function loadInternalPlugins(
170171
)
171172
)
172173

173-
return plugins
174+
const uniquePlugins = uniqWith(plugins, isEqual)
175+
176+
return uniquePlugins
174177
}

packages/gatsby/src/bootstrap/load-themes/__tests__/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const loadThemes = require(`..`)
1+
const { loadThemes } = require(`..`)
22
const path = require(`path`)
33

44
describe(`loadThemes`, () => {

packages/gatsby/src/bootstrap/load-themes/index.js renamed to packages/gatsby/src/bootstrap/load-themes/index.ts

Lines changed: 103 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
1-
const { createRequireFromPath } = require(`gatsby-core-utils`)
2-
const path = require(`path`)
3-
import { mergeGatsbyConfig } from "../../utils/merge-gatsby-config"
4-
const Promise = require(`bluebird`)
5-
const _ = require(`lodash`)
6-
const debug = require(`debug`)(`gatsby:load-themes`)
1+
import { createRequireFromPath } from "gatsby-core-utils"
2+
import * as path from "path"
3+
import {
4+
IGatsbyConfigInput,
5+
mergeGatsbyConfig,
6+
PluginEntry,
7+
IPluginEntryWithParentDir,
8+
} from "../../utils/merge-gatsby-config"
9+
import { mapSeries } from "bluebird"
10+
import { flattenDeep, isEqual, isFunction, uniqWith } from "lodash"
11+
import DebugCtor from "debug"
712
import { preferDefault } from "../prefer-default"
813
import { getConfigFile } from "../get-config-file"
914
import { resolvePlugin } from "../load-plugins/resolve-plugin"
10-
const reporter = require(`gatsby-cli/lib/reporter`)
15+
import reporter from "gatsby-cli/lib/reporter"
16+
17+
const debug = DebugCtor(`gatsby:load-themes`)
18+
19+
interface IThemeObj {
20+
themeName: string
21+
themeConfig: IGatsbyConfigInput
22+
themeDir: string
23+
themeSpec: PluginEntry
24+
parentDir: string
25+
configFilePath?: string
26+
}
1127

1228
// get the gatsby-config file for a theme
1329
const resolveTheme = async (
14-
themeSpec,
15-
configFileThatDeclaredTheme,
16-
isMainConfig = false,
17-
rootDir
18-
) => {
19-
const themeName = themeSpec.resolve || themeSpec
30+
themeSpec: PluginEntry,
31+
configFileThatDeclaredTheme: string | undefined,
32+
isMainConfig: boolean = false,
33+
rootDir: string
34+
): Promise<IThemeObj> => {
35+
const themeName =
36+
typeof themeSpec === `string` ? themeSpec : themeSpec.resolve
2037
let themeDir
2138
try {
2239
const scopedRequire = createRequireFromPath(`${rootDir}/:internal:`)
@@ -59,13 +76,16 @@ const resolveTheme = async (
5976
themeDir,
6077
`gatsby-config`
6178
)
62-
const theme = preferDefault(configModule)
79+
const theme:
80+
| IGatsbyConfigInput
81+
| ((options?: Record<string, unknown>) => IGatsbyConfigInput) =
82+
preferDefault(configModule)
6383

6484
// if theme is a function, call it with the themeConfig
65-
let themeConfig = theme
66-
if (_.isFunction(theme)) {
67-
themeConfig = theme(themeSpec.options || {})
68-
}
85+
const themeConfig = isFunction(theme)
86+
? theme(typeof themeSpec === `string` ? {} : themeSpec.options)
87+
: theme
88+
6989
return {
7090
themeName,
7191
themeConfig,
@@ -84,34 +104,63 @@ const resolveTheme = async (
84104
// no use case for a loop so I expect that to only happen if someone is very
85105
// off track and creating their own set of themes
86106
const processTheme = (
87-
{ themeName, themeConfig, themeSpec, themeDir, configFilePath },
88-
{ rootDir }
89-
) => {
107+
{ themeName, themeConfig, themeSpec, themeDir, configFilePath }: IThemeObj,
108+
{ rootDir }: { rootDir: string }
109+
): Promise<Array<IThemeObj>> => {
90110
const themesList = themeConfig && themeConfig.plugins
91111
// Gatsby themes don't have to specify a gatsby-config.js (they might only use gatsby-node, etc)
92112
// in this case they're technically plugins, but we should support it anyway
93113
// because we can't guarantee which files theme creators create first
94114
if (themeConfig && themesList) {
95115
// for every parent theme a theme defines, resolve the parent's
96116
// gatsby config and return it in order [parentA, parentB, child]
97-
return Promise.mapSeries(themesList, async spec => {
98-
const themeObj = await resolveTheme(spec, configFilePath, false, themeDir)
99-
return processTheme(themeObj, { rootDir: themeDir })
100-
}).then(arr =>
101-
arr.concat([
102-
{ themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir },
103-
])
117+
return mapSeries(
118+
themesList,
119+
async (spec: PluginEntry): Promise<Array<IThemeObj>> => {
120+
const themeObj = await resolveTheme(
121+
spec,
122+
configFilePath,
123+
false,
124+
themeDir
125+
)
126+
return processTheme(themeObj, { rootDir: themeDir })
127+
}
128+
).then(arr =>
129+
flattenDeep(
130+
arr.concat([
131+
{ themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir },
132+
])
133+
)
104134
)
105135
} else {
106136
// if a theme doesn't define additional themes, return the original theme
107-
return [{ themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir }]
137+
return Promise.resolve([
138+
{ themeName, themeConfig, themeSpec, themeDir, parentDir: rootDir },
139+
])
108140
}
109141
}
110142

111-
module.exports = async (config, { configFilePath, rootDir }) => {
112-
const themesA = await Promise.mapSeries(
143+
function normalizePluginEntry(
144+
plugin: PluginEntry,
145+
parentDir: string
146+
): IPluginEntryWithParentDir {
147+
return {
148+
resolve: typeof plugin === `string` ? plugin : plugin.resolve,
149+
options: typeof plugin === `string` ? {} : plugin.options || {},
150+
parentDir,
151+
}
152+
}
153+
154+
export async function loadThemes(
155+
config: IGatsbyConfigInput,
156+
{ configFilePath, rootDir }: { configFilePath: string; rootDir: string }
157+
): Promise<{
158+
config: IGatsbyConfigInput
159+
themes: Array<IThemeObj>
160+
}> {
161+
const themesA = await mapSeries(
113162
config.plugins || [],
114-
async themeSpec => {
163+
async (themeSpec: PluginEntry) => {
115164
const themeObj = await resolveTheme(
116165
themeSpec,
117166
configFilePath,
@@ -120,7 +169,7 @@ module.exports = async (config, { configFilePath, rootDir }) => {
120169
)
121170
return processTheme(themeObj, { rootDir })
122171
}
123-
).then(arr => _.flattenDeep(arr))
172+
).then(arr => flattenDeep(arr))
124173

125174
// log out flattened themes list to aid in debugging
126175
debug(themesA)
@@ -129,21 +178,21 @@ module.exports = async (config, { configFilePath, rootDir }) => {
129178
// list in the config for the theme. This enables the usage of
130179
// gatsby-node, etc in themes.
131180
return (
132-
Promise.mapSeries(
181+
mapSeries(
133182
themesA,
134183
({ themeName, themeConfig = {}, themeSpec, themeDir, parentDir }) => {
135184
return {
136185
...themeConfig,
137186
plugins: [
138-
...(themeConfig.plugins || []).map(plugin => {
139-
return {
140-
resolve: typeof plugin === `string` ? plugin : plugin.resolve,
141-
options: plugin.options || {},
142-
parentDir: themeDir,
143-
}
144-
}),
187+
...(themeConfig.plugins || []).map(plugin =>
188+
normalizePluginEntry(plugin, themeDir)
189+
),
145190
// theme plugin is last so it's gatsby-node, etc can override it's declared plugins, like a normal site.
146-
{ resolve: themeName, options: themeSpec.options || {}, parentDir },
191+
{
192+
resolve: themeName,
193+
options: typeof themeSpec === `string` ? {} : themeSpec.options,
194+
parentDir,
195+
},
147196
],
148197
}
149198
}
@@ -156,8 +205,19 @@ module.exports = async (config, { configFilePath, rootDir }) => {
156205
*/
157206
.reduce(mergeGatsbyConfig, {})
158207
.then(newConfig => {
208+
const mergedConfig = mergeGatsbyConfig(newConfig, {
209+
...config,
210+
plugins: [
211+
...(config.plugins || []).map(plugin =>
212+
normalizePluginEntry(plugin, rootDir)
213+
),
214+
],
215+
})
216+
217+
mergedConfig.plugins = uniqWith(mergedConfig.plugins, isEqual)
218+
159219
return {
160-
config: mergeGatsbyConfig(newConfig, config),
220+
config: mergedConfig,
161221
themes: themesA,
162222
}
163223
})

packages/gatsby/src/schema/graphql-engine/entry.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,20 @@ export class GraphQLEngine {
5959
payload: flattenedPlugins,
6060
})
6161

62-
for (const pluginName of Object.keys(gatsbyNodes)) {
62+
for (const plugin of gatsbyNodes) {
63+
const { name, module, importKey } = plugin
6364
setGatsbyPluginCache(
64-
{ name: pluginName, resolve: `` },
65+
{ name, resolve: ``, importKey },
6566
`gatsby-node`,
66-
gatsbyNodes[pluginName]
67+
module
6768
)
6869
}
69-
for (const pluginName of Object.keys(gatsbyWorkers)) {
70+
for (const plugin of gatsbyWorkers) {
71+
const { name, module, importKey } = plugin
7072
setGatsbyPluginCache(
71-
{ name: pluginName, resolve: `` },
73+
{ name, resolve: ``, importKey },
7274
`gatsby-worker`,
73-
gatsbyWorkers[pluginName]
75+
module
7476
)
7577
}
7678

packages/gatsby/src/schema/graphql-engine/print-plugins.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,24 @@ function render(
5454
usedPlugins: IGatsbyState["flattenedPlugins"],
5555
usedSubPlugins: IGatsbyState["flattenedPlugins"]
5656
): string {
57-
const uniqGatsbyNode = uniq(usedPlugins)
5857
const uniqSubPlugins = uniq(usedSubPlugins)
5958

60-
const sanitizedUsedPlugins = usedPlugins.map(plugin => {
59+
const sanitizedUsedPlugins = usedPlugins.map((plugin, i) => {
6160
// TODO: We don't support functions in pluginOptions here
6261
return {
6362
...plugin,
6463
resolve: ``,
6564
pluginFilepath: ``,
6665
subPluginPaths: undefined,
66+
importKey: i + 1,
6767
}
6868
})
6969

7070
const pluginsWithWorkers = filterPluginsWithWorkers(uniqGatsbyNode)
7171

7272
const subPluginModuleToImportNameMapping = new Map<string, string>()
7373
const imports: Array<string> = [
74-
...uniqGatsbyNode.map(
74+
...usedPlugins.map(
7575
(plugin, i) =>
7676
`import * as pluginGatsbyNode${i} from "${relativePluginPath(
7777
plugin.resolve
@@ -91,22 +91,28 @@ function render(
9191
)}"`
9292
}),
9393
]
94-
const gatsbyNodeExports = uniqGatsbyNode.map(
95-
(plugin, i) => `"${plugin.name}": pluginGatsbyNode${i},`
94+
const gatsbyNodeExports = usedPlugins.map(
95+
(plugin, i) =>
96+
`{ name: "${plugin.name}", module: pluginGatsbyNode${i}, importKey: ${
97+
i + 1
98+
} },`
9699
)
97100
const gatsbyWorkerExports = pluginsWithWorkers.map(
98-
(plugin, i) => `"${plugin.name}": pluginGatsbyWorker${i},`
101+
(plugin, i) =>
102+
`{ name: "${plugin.name}", module: pluginGatsbyWorker${i}, importKey: ${
103+
i + 1
104+
} },`
99105
)
100106
const output = `
101107
${imports.join(`\n`)}
102108
103-
export const gatsbyNodes = {
109+
export const gatsbyNodes = [
104110
${gatsbyNodeExports.join(`\n`)}
105-
}
111+
]
106112
107-
export const gatsbyWorkers = {
113+
export const gatsbyWorkers = [
108114
${gatsbyWorkerExports.join(`\n`)}
109-
}
115+
]
110116
111117
export const flattenedPlugins =
112118
${JSON.stringify(

packages/gatsby/src/schema/graphql-engine/standalone-regenerate.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { store } from "../../redux"
2626
import { validateEngines } from "../../utils/validate-engines"
2727

2828
async function run(): Promise<void> {
29+
process.env.GATSBY_SLICES = `1`
2930
// load config
3031
console.log(`loading config and plugins`)
3132
await loadConfigAndPlugins({

0 commit comments

Comments
 (0)