Skip to content

Commit 26f2b72

Browse files
authored
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
1 parent 6f0e0c7 commit 26f2b72

File tree

11 files changed

+153
-166
lines changed

11 files changed

+153
-166
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
@@ -218,9 +218,7 @@ describe(`Load plugins`, () => {
218218
(plugin: { name: string }) => plugin.name === `gatsby-plugin-typescript`
219219
)
220220

221-
// TODO: I think we should probably be de-duping, so this should be 1.
222-
// But this test is mostly here to ensure we don't add an _additional_ gatsby-plugin-typescript
223-
expect(tsplugins.length).toEqual(2)
221+
expect(tsplugins.length).toEqual(1)
224222
})
225223
})
226224

@@ -351,9 +349,7 @@ describe(`Load plugins`, () => {
351349
plugin.name === `gatsby-plugin-gatsby-cloud`
352350
)
353351

354-
// TODO: I think we should probably be de-duping, so this should be 1.
355-
// But this test is mostly here to ensure we don't add an _additional_ gatsby-plugin-typescript
356-
expect(cloudPlugins.length).toEqual(2)
352+
expect(cloudPlugins.length).toEqual(1)
357353
})
358354
})
359355

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: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,24 @@ async function render(
5353
usedPlugins: IGatsbyState["flattenedPlugins"],
5454
usedSubPlugins: IGatsbyState["flattenedPlugins"]
5555
): Promise<string> {
56-
const uniqGatsbyNode = uniq(usedPlugins)
5756
const uniqSubPlugins = uniq(usedSubPlugins)
5857

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

69-
const pluginsWithWorkers = await filterPluginsWithWorkers(uniqGatsbyNode)
69+
const pluginsWithWorkers = await filterPluginsWithWorkers(usedPlugins)
7070

7171
const subPluginModuleToImportNameMapping = new Map<string, string>()
7272
const imports: Array<string> = [
73-
...uniqGatsbyNode.map(
73+
...usedPlugins.map(
7474
(plugin, i) =>
7575
`import * as pluginGatsbyNode${i} from "${relativePluginPath(
7676
plugin.resolve
@@ -90,22 +90,28 @@ async function render(
9090
)}"`
9191
}),
9292
]
93-
const gatsbyNodeExports = uniqGatsbyNode.map(
94-
(plugin, i) => `"${plugin.name}": pluginGatsbyNode${i},`
93+
const gatsbyNodeExports = usedPlugins.map(
94+
(plugin, i) =>
95+
`{ name: "${plugin.name}", module: pluginGatsbyNode${i}, importKey: ${
96+
i + 1
97+
} },`
9598
)
9699
const gatsbyWorkerExports = pluginsWithWorkers.map(
97-
(plugin, i) => `"${plugin.name}": pluginGatsbyWorker${i},`
100+
(plugin, i) =>
101+
`{ name: "${plugin.name}", module: pluginGatsbyWorker${i}, importKey: ${
102+
i + 1
103+
} },`
98104
)
99105
const output = `
100106
${imports.join(`\n`)}
101107
102-
export const gatsbyNodes = {
108+
export const gatsbyNodes = [
103109
${gatsbyNodeExports.join(`\n`)}
104-
}
110+
]
105111
106-
export const gatsbyWorkers = {
112+
export const gatsbyWorkers = [
107113
${gatsbyWorkerExports.join(`\n`)}
108-
}
114+
]
109115
110116
export const flattenedPlugins =
111117
${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)