Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core): Make load plugins modular, prepare for TS #34813

Merged
merged 6 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions integration-tests/gatsby-cli/__tests__/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ describe(`gatsby build`, () => {
it(`creates a built gatsby site`, () => {
const [code, logs] = GatsbyCLI.from(cwd).invoke(`build`)

logs.should.contain(
`success open and validate gatsby-configs, load plugins`
)
logs.should.contain(`success load gatsby config`)
logs.should.contain(`success load plugins`)
logs.should.contain(`success onPreInit`)
logs.should.contain(`success initialize cache`)
logs.should.contain(`success copy gatsby files`)
Expand Down
5 changes: 2 additions & 3 deletions integration-tests/gatsby-cli/__tests__/develop.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ describe(`gatsby develop`, () => {

// 3. Make sure logs for the user contain expected results
const logs = getLogs()
logs.should.contain(
`success open and validate gatsby-configs, load plugins`
)
logs.should.contain(`success load gatsby config`)
logs.should.contain(`success load plugins`)
logs.should.contain(`success onPreInit`)
logs.should.contain(`success initialize cache`)
logs.should.contain(`success copy gatsby files`)
Expand Down
5 changes: 2 additions & 3 deletions integration-tests/gatsby-cli/__tests__/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ describe(`gatsby repl`, () => {

// 3. Make assertions
const logs = getLogs()
logs.should.contain(
`success open and validate gatsby-configs, load plugins`
)
logs.should.contain(`success load gatsby config`)
logs.should.contain(`success load plugins`)
logs.should.contain(`success onPreInit`)
logs.should.contain(`success initialize cache`)
logs.should.contain(`success copy gatsby files`)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
import reporter from "gatsby-cli/lib/reporter"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was renamed from load-config-and-plugins to load-config/index.ts and now exports loadConfig

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 { store } from "../../redux"
import handleFlags from "../../utils/handle-flags"
import availableFlags from "../../utils/flags"
import { IProgram } from "../../commands/types"
import { IGatsbyConfig } from "../../internal"

import { IFlattenedPlugin } from "./load-plugins/types"

import { preferDefault } from "../bootstrap/prefer-default"
import { getConfigFile } from "../bootstrap/get-config-file"
import { loadPlugins } from "../bootstrap/load-plugins"
import { internalActions } from "../redux/actions"
import loadThemes from "../bootstrap/load-themes"
import { store } from "../redux"
import handleFlags from "../utils/handle-flags"
import availableFlags from "../utils/flags"
import { IProgram } from "../commands/types"

export async function loadConfigAndPlugins({
export async function loadConfig({
siteDirectory,
processFlags = false,
}: {
siteDirectory: string
processFlags?: boolean
program?: IProgram
}): Promise<{
config: any
flattenedPlugins: Array<IFlattenedPlugin>
}> {
}): Promise<IGatsbyConfig> {
// Try opening the site's gatsby-config.js file.
const { configModule, configFilePath } = await getConfigFile(
siteDirectory,
Expand Down Expand Up @@ -94,7 +88,5 @@ export async function loadConfigAndPlugins({

store.dispatch(internalActions.setSiteConfig(config))

const flattenedPlugins = await loadPlugins(config, siteDirectory)

return { config, flattenedPlugins }
return config
}
98 changes: 8 additions & 90 deletions packages/gatsby/src/bootstrap/load-plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,105 +1,23 @@
import _ from "lodash"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only functions were extracted out of this file and put into their own files


import { store } from "../../redux"
import { IGatsbyState } from "../../redux/types"
import * as nodeAPIs from "../../utils/api-node-docs"
import * as browserAPIs from "../../utils/api-browser-docs"
import ssrAPIs from "../../../cache-dir/api-ssr-docs"
import { loadPlugins as loadPluginsInternal } from "./load"
import { loadInternalPlugins } from "./load-internal-plugins"
import {
collatePluginAPIs,
handleBadExports,
handleMultipleReplaceRenderers,
ExportType,
ICurrentAPIs,
validateConfigPluginsOptions,
} from "./validate"
import {
IPluginInfo,
IFlattenedPlugin,
ISiteConfig,
IRawSiteConfig,
} from "./types"
import { IPluginRefObject, PluginRef } from "gatsby-plugin-utils/dist/types"

const getAPI = (api: {
[exportType in ExportType]: { [api: string]: boolean }
}): ICurrentAPIs =>
_.keys(api).reduce<Partial<ICurrentAPIs>>((merged, key) => {
merged[key] = _.keys(api[key])
return merged
}, {}) as ICurrentAPIs

// Create a "flattened" array of plugins with all subplugins
// brought to the top-level. This simplifies running gatsby-* files
// for subplugins.
const flattenPlugins = (plugins: Array<IPluginInfo>): Array<IPluginInfo> => {
const flattened: Array<IPluginInfo> = []
const extractPlugins = (plugin: IPluginInfo): void => {
if (plugin.subPluginPaths) {
for (const subPluginPath of plugin.subPluginPaths) {
// @pieh:
// subPluginPath can look like someOption.randomFieldThatIsMarkedAsSubplugins
// Reason for doing stringified path with . separator was that it was just easier to prevent duplicates
// in subPluginPaths array (as each subplugin in the gatsby-config would add subplugin path).
const segments = subPluginPath.split(`.`)
let roots: Array<any> = [plugin.pluginOptions]
for (const segment of segments) {
if (segment === `[]`) {
roots = roots.flat()
} else {
roots = roots.map(root => root[segment])
}
}
roots = roots.flat()

roots.forEach(subPlugin => {
flattened.push(subPlugin)
extractPlugins(subPlugin)
})
}
}
}

plugins.forEach(plugin => {
flattened.push(plugin)
extractPlugins(plugin)
})

return flattened
}

function normalizePlugin(plugin): IPluginRefObject {
if (typeof plugin === `string`) {
return {
resolve: plugin,
options: {},
}
}

if (plugin.options?.plugins) {
plugin.options = {
...plugin.options,
plugins: normalizePlugins(plugin.options.plugins),
}
}

return plugin
}

function normalizePlugins(plugins?: Array<PluginRef>): Array<IPluginRefObject> {
return (plugins || []).map(normalizePlugin)
}

const normalizeConfig = (config: IRawSiteConfig = {}): ISiteConfig => {
return {
...config,
plugins: (config.plugins || []).map(normalizePlugin),
}
}
import { IFlattenedPlugin } from "./types"
import { normalizeConfig } from "./utils/normalize"
import { getAPI } from "./utils/get-api"
import { flattenPlugins } from "./utils/flatten-plugins"
import { IGatsbyConfig } from "../../internal"

export async function loadPlugins(
rawConfig: IRawSiteConfig = {},
rawConfig: IGatsbyConfig,
rootDir: string
): Promise<Array<IFlattenedPlugin>> {
// Turn all strings in plugins: [`...`] into the { resolve: ``, options: {} } form
Expand All @@ -115,7 +33,7 @@ export async function loadPlugins(
})

// Collate internal plugins, site config plugins, site default plugins
const pluginInfos = loadPluginsInternal(config, rootDir)
const pluginInfos = loadInternalPlugins(config, rootDir)

// Create a flattened array of the plugins
const pluginArray = flattenPlugins(pluginInfos)
Expand Down
154 changes: 154 additions & 0 deletions packages/gatsby/src/bootstrap/load-plugins/load-internal-plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { slash } from "gatsby-core-utils"
Copy link
Contributor Author

@tyhopp tyhopp Feb 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file used to be load-plugins/load and was imported elsewhere like import { loadPlugins as loadPluginsInternal } from "./load". Name is was changed to be explicit

import path from "path"
import reporter from "gatsby-cli/lib/reporter"
import { store } from "../../redux"
import {
IPluginInfo,
IPluginRefObject,
IPluginRefOptions,
ISiteConfig,
} from "./types"
import { processPlugin } from "./process-plugin"
import { createPluginId } from "./utils/create-id"
import { createFileContentHash } from "./utils/create-hash"
import {
addGatsbyPluginCloudPluginWhenInstalled,
incompatibleGatsbyCloudPlugin,
GATSBY_CLOUD_PLUGIN_NAME,
} from "./utils/handle-gatsby-cloud"

const TYPESCRIPT_PLUGIN_NAME = `gatsby-plugin-typescript`

export function loadInternalPlugins(
config: ISiteConfig = {},
rootDir: string
): Array<IPluginInfo> {
// Instantiate plugins.
const plugins: Array<IPluginInfo> = []
const configuredPluginNames = new Set()

// Add internal plugins
const internalPlugins = [
`../../internal-plugins/dev-404-page`,
`../../internal-plugins/load-babel-config`,
`../../internal-plugins/internal-data-bridge`,
`../../internal-plugins/prod-404-500`,
`../../internal-plugins/webpack-theme-component-shadowing`,
`../../internal-plugins/bundle-optimisations`,
`../../internal-plugins/functions`,
].filter(Boolean) as Array<string>

internalPlugins.forEach(relPath => {
const absPath = path.join(__dirname, relPath)
plugins.push(processPlugin(absPath, rootDir))
})

// Add plugins from the site config.
if (config.plugins) {
config.plugins.forEach(plugin => {
const processedPlugin = processPlugin(plugin, rootDir)
plugins.push(processedPlugin)
configuredPluginNames.add(processedPlugin.name)
})
}

// the order of all of these page-creators matters. The "last plugin wins",
// so the user's site comes last, and each page-creator instance has to
// match the plugin definition order before that. This works fine for themes
// because themes have already been added in the proper order to the plugins
// array
plugins.forEach(plugin => {
plugins.push(
processPlugin(
{
resolve: require.resolve(`gatsby-plugin-page-creator`),
options: {
path: slash(path.join(plugin.resolve, `src/pages`)),
pathCheck: false,
},
},
rootDir
)
)
})

if (
_CFLAGS_.GATSBY_MAJOR === `4` &&
configuredPluginNames.has(GATSBY_CLOUD_PLUGIN_NAME) &&
incompatibleGatsbyCloudPlugin(plugins)
) {
reporter.panic(
`Plugin gatsby-plugin-gatsby-cloud is not compatible with your gatsby version. Please upgrade to gatsby-plugin-gatsby-cloud@next`
)
}

if (
!configuredPluginNames.has(GATSBY_CLOUD_PLUGIN_NAME) &&
(process.env.GATSBY_CLOUD === `true` || process.env.GATSBY_CLOUD === `1`)
) {
addGatsbyPluginCloudPluginWhenInstalled(plugins, rootDir)
}

// Support Typescript by default but allow users to override it
if (!configuredPluginNames.has(TYPESCRIPT_PLUGIN_NAME)) {
const processedTypeScriptPlugin = processPlugin(
{
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
allExtensions: false,
isTSX: false,
jsxPragma: `React`,
},
},
rootDir
)
plugins.push(processedTypeScriptPlugin)
}

// Add the site's default "plugin" i.e. gatsby-x files in root of site.
plugins.push({
resolve: slash(process.cwd()),
id: createPluginId(`default-site-plugin`),
name: `default-site-plugin`,
version: createFileContentHash(process.cwd(), `gatsby-*`),
pluginOptions: {
plugins: [],
},
})

const program = store.getState().program

// default options for gatsby-plugin-page-creator
let pageCreatorOptions: IPluginRefOptions | undefined = {
path: slash(path.join(program.directory, `src/pages`)),
pathCheck: false,
}

if (config.plugins) {
const pageCreatorPlugin = config.plugins.find(
(plugin): plugin is IPluginRefObject =>
typeof plugin !== `string` &&
plugin.resolve === `gatsby-plugin-page-creator` &&
slash((plugin.options && plugin.options.path) || ``) ===
slash(path.join(program.directory, `src/pages`))
)
if (pageCreatorPlugin) {
// override the options if there are any user specified options
pageCreatorOptions = pageCreatorPlugin.options
}
}

const processedPageCreatorPlugin = processPlugin(
{
resolve: require.resolve(`gatsby-plugin-page-creator`),
options: pageCreatorOptions,
},
rootDir
)

plugins.push(processedPageCreatorPlugin)

return plugins
}
Loading