diff --git a/packages/gatsby/src/bootstrap/load-themes/index.js b/packages/gatsby/src/bootstrap/load-themes/index.js index 183e6920c82ab..733de249bcc11 100644 --- a/packages/gatsby/src/bootstrap/load-themes/index.js +++ b/packages/gatsby/src/bootstrap/load-themes/index.js @@ -1,6 +1,6 @@ const { createRequireFromPath } = require(`gatsby-core-utils`) const path = require(`path`) -const mergeGatsbyConfig = require(`../../utils/merge-gatsby-config`) +import { mergeGatsbyConfig } from "../../utils/merge-gatsby-config" const Promise = require(`bluebird`) const _ = require(`lodash`) const debug = require(`debug`)(`gatsby:load-themes`) diff --git a/packages/gatsby/src/redux/types.ts b/packages/gatsby/src/redux/types.ts index 040d869cf69d7..3c6fb66f0126a 100644 --- a/packages/gatsby/src/redux/types.ts +++ b/packages/gatsby/src/redux/types.ts @@ -57,6 +57,7 @@ export interface IGatsbyConfig { developMiddleware?: any proxy?: any pathPrefix?: string + mapping?: Record } export interface IGatsbyNode { diff --git a/packages/gatsby/src/utils/__tests__/merge-gatsby-config.js b/packages/gatsby/src/utils/__tests__/merge-gatsby-config.ts similarity index 98% rename from packages/gatsby/src/utils/__tests__/merge-gatsby-config.js rename to packages/gatsby/src/utils/__tests__/merge-gatsby-config.ts index 6fee0ae779ef4..9fe5959f5b17d 100644 --- a/packages/gatsby/src/utils/__tests__/merge-gatsby-config.js +++ b/packages/gatsby/src/utils/__tests__/merge-gatsby-config.ts @@ -1,4 +1,4 @@ -const mergeGatsbyConfig = require(`../merge-gatsby-config`) +import { mergeGatsbyConfig } from "../merge-gatsby-config" describe(`Merge gatsby config`, () => { it(`Merging empty config is an identity operation`, () => { diff --git a/packages/gatsby/src/utils/merge-gatsby-config.js b/packages/gatsby/src/utils/merge-gatsby-config.ts similarity index 54% rename from packages/gatsby/src/utils/merge-gatsby-config.js rename to packages/gatsby/src/utils/merge-gatsby-config.ts index ef58ca7b2add5..baad8d1a747b0 100644 --- a/packages/gatsby/src/utils/merge-gatsby-config.js +++ b/packages/gatsby/src/utils/merge-gatsby-config.ts @@ -1,31 +1,36 @@ -const _ = require(`lodash`) -/** - * Defines how a theme object is merged with the user's config - */ -module.exports = (a, b) => { - // a and b are gatsby configs, If they have keys, that means there are values to merge - const allGatsbyConfigKeysWithAValue = _.uniq( - Object.keys(a).concat(Object.keys(b)) - ) +import _ from "lodash" +import { Express } from "express" +// TODO export it in index.d.ts +type PluginEntry = + | string + | { + resolve: string + options?: Record + } - // reduce the array of mergable keys into a single gatsby config object - const mergedConfig = allGatsbyConfigKeysWithAValue.reduce( - (config, gatsbyConfigKey) => { - // choose a merge function for the config key if there's one defined, - // otherwise use the default value merge function - const mergeFn = howToMerge[gatsbyConfigKey] || howToMerge.byDefault - return { - ...config, - [gatsbyConfigKey]: mergeFn(a[gatsbyConfigKey], b[gatsbyConfigKey]), - } - }, - {} - ) +interface INormalizedPluginEntry { + resolve: string + options: Record +} - // return the fully merged config - return mergedConfig +interface IGatsbyConfigInput { + siteMetadata?: Record + plugins?: Array + pathPrefix?: string + assetPrefix?: string + polyfill?: boolean + mapping?: Record + proxy?: { + prefix: string + url: string + } + developMiddleware?(app: Express): void } +type ConfigKey = keyof IGatsbyConfigInput +type Metadata = IGatsbyConfigInput["siteMetadata"] +type Mapping = IGatsbyConfigInput["mapping"] + /** * Normalize plugin spec before comparing so * - `gatsby-plugin-name` @@ -33,9 +38,12 @@ module.exports = (a, b) => { * - { resolve: `gatsby-plugin-name`, options: {} } * are all considered equal */ -const normalizePluginEntry = entry => +const normalizePluginEntry = (entry: PluginEntry): INormalizedPluginEntry => _.isString(entry) - ? { resolve: entry, options: {} } + ? { + resolve: entry, + options: {}, + } : _.isObject(entry) ? { options: {}, ...entry } : entry @@ -46,15 +54,46 @@ const howToMerge = { * This makes sure that if a single value is defined, that one it used. * We prefer the "right" value, because the user's config will be "on the right" */ - byDefault: (a, b) => b || a, - siteMetadata: (objA, objB) => _.merge({}, objA, objB), + byDefault: (a: ConfigKey, b: ConfigKey): ConfigKey => b || a, + siteMetadata: (objA: Metadata, objB: Metadata): Metadata => + _.merge({}, objA, objB), // plugins are concatenated and uniq'd, so we don't get two of the same plugin value - plugins: (a = [], b = []) => + plugins: (a: PluginEntry[] = [], b: PluginEntry[] = []): PluginEntry[] => _.uniqWith(a.concat(b), (a, b) => _.isEqual( _.pick(normalizePluginEntry(a), [`resolve`, `options`]), _.pick(normalizePluginEntry(b), [`resolve`, `options`]) ) ), - mapping: (objA, objB) => _.merge({}, objA, objB), + mapping: (objA: Mapping, objB: Mapping): Mapping => _.merge({}, objA, objB), +} as const + +/** + * Defines how a theme object is merged with the user's config + */ +export const mergeGatsbyConfig = ( + a: IGatsbyConfigInput, + b: IGatsbyConfigInput +): IGatsbyConfigInput => { + // a and b are gatsby configs, If they have keys, that means there are values to merge + const allGatsbyConfigKeysWithAValue = _.uniq( + Object.keys(a).concat(Object.keys(b)) + ) as ConfigKey[] + + // reduce the array of mergable keys into a single gatsby config object + const mergedConfig = allGatsbyConfigKeysWithAValue.reduce( + (config, gatsbyConfigKey) => { + // choose a merge function for the config key if there's one defined, + // otherwise use the default value merge function + const mergeFn = howToMerge[gatsbyConfigKey] || howToMerge.byDefault + return { + ...config, + [gatsbyConfigKey]: mergeFn(a[gatsbyConfigKey], b[gatsbyConfigKey]), + } + }, + {} as IGatsbyConfigInput + ) + + // return the fully merged config + return mergedConfig }