From 240a33fdac6bff18305a2ba957f6865c7c13d622 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 12 Nov 2020 11:23:55 +0000 Subject: [PATCH 1/3] feat(create-gatsby): Add support for plugin dependencies --- packages/create-gatsby/src/features.json | 52 +++++++++++++--- packages/create-gatsby/src/index.ts | 61 +++++++++++++++---- packages/create-gatsby/src/install-plugins.ts | 3 +- packages/gatsby-cli/src/plugin-add.ts | 7 ++- 4 files changed, 101 insertions(+), 22 deletions(-) diff --git a/packages/create-gatsby/src/features.json b/packages/create-gatsby/src/features.json index 8bc78252ff1dd..7087fe6617531 100644 --- a/packages/create-gatsby/src/features.json +++ b/packages/create-gatsby/src/features.json @@ -1,9 +1,47 @@ { - "gatsby-plugin-google-analytics": {"message": "Add the Google Analytics tracking script"}, - "gatsby-plugin-react-helmet": {"message": "Add page metadata", "dependencies": ["react-helmet"]}, - "gatsby-plugin-sitemap": {"message": "Add an automatic sitemap"}, - "gatsby-plugin-offline": {"message": "Enable offline functionality"}, - "gatsby-plugin-manifest": {"message": "Generate a manifest file"}, - "gatsby-plugin-mdx": {"message": "Add MDX support", "dependencies": ["@mdx-js/react", "@mdx-js/mdx"]} - + "gatsby-plugin-google-analytics": { + "message": "Add the Google Analytics tracking script" + }, + "gatsby-plugin-image": { + "message": "Add responsive images", + "plugins": [ + "gatsby-plugin-sharp", + "gatsby-transformer-sharp", + "gatsby-source-filesystem:images" + ], + "options": { + "gatsby-source-filesystem:images": { + "name": "images", + "path": "./src/images/" + } + } + }, + "gatsby-plugin-react-helmet": { + "message": "Add page meta tags with React Helmet", + "dependencies": ["react-helmet"] + }, + "gatsby-plugin-sitemap": { "message": "Add an automatic sitemap" }, + "gatsby-plugin-offline": { "message": "Enable offline functionality" }, + "gatsby-plugin-manifest": { "message": "Generate a manifest file" }, + "gatsby-transformer-remark": { + "message": "Add Markdown support (without MDX)", + "plugins": ["gatsby-source-filesystem:pages"], + "options": { + "gatsby-source-filesystem:pages": { + "name": "pages", + "path": "./src/pages/" + } + } + }, + "gatsby-plugin-mdx": { + "message": "Add Markdown and MDX support", + "plugins": ["gatsby-source-filesystem:pages"], + "dependencies": ["@mdx-js/react", "@mdx-js/mdx"], + "options": { + "gatsby-source-filesystem:pages": { + "name": "pages", + "path": "./src/pages/" + } + } + } } diff --git a/packages/create-gatsby/src/index.ts b/packages/create-gatsby/src/index.ts index 48a3b5b2ee882..5f4f45d347ca2 100644 --- a/packages/create-gatsby/src/index.ts +++ b/packages/create-gatsby/src/index.ts @@ -10,6 +10,7 @@ import fs from "fs" import { plugin } from "./components/plugin" import { makePluginConfigQuestions } from "./plugin-options-form" import { center, rule, wrap } from "./components/utils" +import { stripIndent } from "common-tags" // eslint-disable-next-line no-control-regex const INVALID_FILENAMES = /[<>:"/\\|?*\u0000-\u001F]/g @@ -81,7 +82,7 @@ export const questions: any = [ type: `multiselectinput`, name: `features`, message: `Would you like to install additional features with other plugins?`, - hint: `(Multiple choice) Use arrow keys to move, spacebar to select, and enter to confirm your choices`, + hint: `(Multiple choice) Use arrow keys to move, enter to select, and choose "Done" to confirm your choices`, choices: makeChoices(features, true), }, ] @@ -93,12 +94,31 @@ interface IAnswers { } interface IPluginEntry { + /** + * Message displayed in the menu when selecting the plugin + */ message: string + /** + * Extra NPM packages to install + */ dependencies?: Array + /** + * Items are either the plugin name, or the plugin name and key, separated by a colon (":") + * This allows duplicate entries for plugins such as gatsby-source-filesystem. + */ + plugins?: Array + /** + * Keys must match plugin names or name:key combinations from the plugins array + */ + options?: PluginConfigMap } export type PluginMap = Record +export type PluginConfigMap = Record> + +const removeKey = (plugin: string): string => plugin.split(`:`)[0] + export async function run(): Promise { const { version } = require(`../package.json`) @@ -141,6 +161,7 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} const plugins: Array = [] const packages: Array = [] + let pluginConfig: PluginConfigMap = {} if (data.cms && data.cms !== `none`) { messages.push( @@ -149,7 +170,12 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} )}` ) plugins.push(data.cms) - packages.push(data.cms, ...(cmses[data.cms].dependencies || [])) + packages.push( + data.cms, + ...(cmses[data.cms].dependencies || []), + ...(cmses[data.cms].plugins || []) + ) + pluginConfig = { ...pluginConfig, ...cmses[data.cms].options } } if (data.styling && data.styling !== `none`) { @@ -159,7 +185,12 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} )} for styling your site` ) plugins.push(data.styling) - packages.push(data.styling, ...(styles[data.styling].dependencies || [])) + packages.push( + data.styling, + ...(styles[data.styling].dependencies || []), + ...(styles[data.styling].plugins || []) + ) + pluginConfig = { ...pluginConfig, ...styles[data.styling].options } } if (data.features?.length) { @@ -169,19 +200,25 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} .join(`, `)}` ) plugins.push(...data.features) - const featureDependencies = data.features?.map( - featureKey => features[featureKey].dependencies || [] - ) + const featureDependencies = data.features?.map(featureKey => [ + // Spread in extra dependencies + ...(features[featureKey].dependencies || []), + // Spread in plugins, stripping the optional key + ...(features[featureKey].plugins || []), + ]) const flattenedDependencies = ([] as Array).concat.apply( [], featureDependencies ) // here until we upgrade to node 11 and can use flatMap packages.push(...data.features, ...flattenedDependencies) + // Merge plugin options + pluginConfig = data.features.reduce((prev, key) => { + return { ...prev, ...features[key].options } + }, pluginConfig) } const config = makePluginConfigQuestions(plugins) - let pluginConfig if (config.length) { console.log( `\nGreat! A few of the selections you made need to be configured. Please fill in the options for each plugin now:\n` @@ -189,7 +226,7 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} const enquirer = new Enquirer>() enquirer.use(plugin) - pluginConfig = await enquirer.prompt(config) + pluginConfig = { ...pluginConfig, ...(await enquirer.prompt(config)) } } console.log(` @@ -226,11 +263,11 @@ ${c.bold(`Thanks! Here's what we'll now do:`)} const runCommand = pm === `npm` ? `npm run` : `yarn` console.log( - `🎉 Your new Gatsby site ${c.bold( + stripIndent` + 🎉 Your new Gatsby site ${c.bold( data.project - )} has been successfully bootstrapped at ${c.bold( - path.resolve(data.project) - )}. + )} has been successfully bootstrapped + at ${c.bold(path.resolve(data.project))}. ` ) console.log(`Start by going to the directory with\n diff --git a/packages/create-gatsby/src/install-plugins.ts b/packages/create-gatsby/src/install-plugins.ts index a360e07f1db53..f87a5d8d4fe64 100644 --- a/packages/create-gatsby/src/install-plugins.ts +++ b/packages/create-gatsby/src/install-plugins.ts @@ -1,8 +1,9 @@ import { reporter } from "./reporter" import path from "path" +import { PluginConfigMap } from "." export async function installPlugins( plugins: Array, - pluginOptions: Record | undefined> = {}, + pluginOptions: PluginConfigMap = {}, rootPath: string, packages: Array ): Promise { diff --git a/packages/gatsby-cli/src/plugin-add.ts b/packages/gatsby-cli/src/plugin-add.ts index ac98d5fa2e61d..925fea2be4dcd 100644 --- a/packages/gatsby-cli/src/plugin-add.ts +++ b/packages/gatsby-cli/src/plugin-add.ts @@ -41,12 +41,15 @@ async function installPluginConfig( `Adding ${plugin} to gatsby-config` ) + // Plugins can optionally include a key, to allow duplicates + const [pluginName, pluginKey] = plugin.split(`:`) + installTimer.start() - reporter.info(`Adding ${plugin}`) + reporter.info(`Adding ${pluginName}`) try { const result = await GatsbyPlugin.create( { root }, - { name: plugin, options } + { name: plugin, options, key: pluginKey } ) reporter.info(result._message) } catch (err) { From 2f7445681b484ae6e728f7f8223f8536107be3c4 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 12 Nov 2020 13:35:58 +0000 Subject: [PATCH 2/3] Fix extra plugin handling --- packages/create-gatsby/src/index.ts | 31 ++++++++++++------- .../create-gatsby/src/plugin-options-form.ts | 2 +- packages/gatsby-cli/src/plugin-add.ts | 8 ++--- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/packages/create-gatsby/src/index.ts b/packages/create-gatsby/src/index.ts index 5f4f45d347ca2..57b8d8b806925 100644 --- a/packages/create-gatsby/src/index.ts +++ b/packages/create-gatsby/src/index.ts @@ -169,11 +169,12 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} cmses[data.cms].message )}` ) - plugins.push(data.cms) + const extraPlugins = cmses[data.cms].plugins || [] + plugins.push(data.cms, ...extraPlugins) packages.push( data.cms, ...(cmses[data.cms].dependencies || []), - ...(cmses[data.cms].plugins || []) + ...extraPlugins ) pluginConfig = { ...pluginConfig, ...cmses[data.cms].options } } @@ -184,11 +185,13 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} styles[data.styling].message )} for styling your site` ) - plugins.push(data.styling) + const extraPlugins = styles[data.styling].plugins || [] + + plugins.push(data.styling, ...extraPlugins) packages.push( data.styling, ...(styles[data.styling].dependencies || []), - ...(styles[data.styling].plugins || []) + ...extraPlugins ) pluginConfig = { ...pluginConfig, ...styles[data.styling].options } } @@ -200,12 +203,16 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} .join(`, `)}` ) plugins.push(...data.features) - const featureDependencies = data.features?.map(featureKey => [ - // Spread in extra dependencies - ...(features[featureKey].dependencies || []), - // Spread in plugins, stripping the optional key - ...(features[featureKey].plugins || []), - ]) + const featureDependencies = data.features?.map(featureKey => { + const extraPlugins = features[featureKey].plugins || [] + plugins.push(...extraPlugins) + return [ + // Spread in extra dependencies + ...(features[featureKey].dependencies || []), + // Spread in plugins, stripping the optional key + ...extraPlugins, + ] + }) const flattenedDependencies = ([] as Array).concat.apply( [], featureDependencies @@ -249,10 +256,10 @@ ${c.bold(`Thanks! Here's what we'll now do:`)} return } - await initStarter(DEFAULT_STARTER, data.project, packages) + await initStarter(DEFAULT_STARTER, data.project, packages.map(removeKey)) console.log(c.green(`✔ `) + `Created site in ` + c.green(data.project)) - + console.log({ plugins, pluginConfig }) if (plugins.length) { console.log(c.bold(`🔌 Installing plugins...`)) await installPlugins(plugins, pluginConfig, path.resolve(data.project), []) diff --git a/packages/create-gatsby/src/plugin-options-form.ts b/packages/create-gatsby/src/plugin-options-form.ts index 8e2e12d90c4bb..5081e51ce51a2 100644 --- a/packages/create-gatsby/src/plugin-options-form.ts +++ b/packages/create-gatsby/src/plugin-options-form.ts @@ -56,7 +56,7 @@ export const makePluginConfigQuestions = ( selectedPlugins.forEach((pluginName: string): void => { const schema = pluginSchemas[pluginName as PluginName] - if (typeof schema === `string` || !(`keys` in schema)) { + if (!schema || typeof schema === `string` || !(`keys` in schema)) { return } const options: Record | undefined = schema?.keys diff --git a/packages/gatsby-cli/src/plugin-add.ts b/packages/gatsby-cli/src/plugin-add.ts index 925fea2be4dcd..3cd2c111650f8 100644 --- a/packages/gatsby-cli/src/plugin-add.ts +++ b/packages/gatsby-cli/src/plugin-add.ts @@ -37,19 +37,19 @@ async function installPluginConfig( options: Record | undefined, root: string ): Promise { + // Plugins can optionally include a key, to allow duplicates + const [pluginName, pluginKey] = plugin.split(`:`) + const installTimer = reporter.activityTimer( `Adding ${plugin} to gatsby-config` ) - // Plugins can optionally include a key, to allow duplicates - const [pluginName, pluginKey] = plugin.split(`:`) - installTimer.start() reporter.info(`Adding ${pluginName}`) try { const result = await GatsbyPlugin.create( { root }, - { name: plugin, options, key: pluginKey } + { name: pluginName, options, key: pluginKey } ) reporter.info(result._message) } catch (err) { From d83a5782c6f6ca470b998460f71d068ff74fe992 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 12 Nov 2020 15:06:49 +0000 Subject: [PATCH 3/3] Fix comment --- packages/create-gatsby/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-gatsby/src/index.ts b/packages/create-gatsby/src/index.ts index 57b8d8b806925..f2b2f62082d64 100644 --- a/packages/create-gatsby/src/index.ts +++ b/packages/create-gatsby/src/index.ts @@ -209,7 +209,7 @@ ${center(c.blueBright.bold.underline(`Welcome to Gatsby!`))} return [ // Spread in extra dependencies ...(features[featureKey].dependencies || []), - // Spread in plugins, stripping the optional key + // Spread in plugins ...extraPlugins, ] })