diff --git a/packages/gatsby-cli/src/create-cli.ts b/packages/gatsby-cli/src/create-cli.ts index 4b5d575fb966e..532bc83cd70f4 100644 --- a/packages/gatsby-cli/src/create-cli.ts +++ b/packages/gatsby-cli/src/create-cli.ts @@ -354,9 +354,29 @@ function buildLocalCommands(cli: yargs.Argv, isLocalSite: boolean): void { cli.command({ command: `recipes [recipe]`, describe: `[EXPERIMENTAL] Run a recipe`, + builder: _ => + _.option(`D`, { + alias: `develop`, + type: `boolean`, + default: false, + describe: `Start recipe in develop mode to live-develop your recipe (defaults to false)`, + }).option(`I`, { + alias: `install`, + type: `boolean`, + default: false, + describe: `Install recipe (defaults to plan mode)`, + }), handler: handlerP( - async ({ recipe }: yargs.Arguments<{ recipe: string | undefined }>) => { - await recipesHandler(siteInfo.directory, recipe) + async ({ + recipe, + develop, + install, + }: yargs.Arguments<{ + recipe: string | undefined + develop: boolean + install: boolean + }>) => { + await recipesHandler(siteInfo.directory, recipe, develop, install) } ), }) diff --git a/packages/gatsby-cli/src/recipes.ts b/packages/gatsby-cli/src/recipes.ts index 79b6504d5b887..fe7af7c1fa3c6 100644 --- a/packages/gatsby-cli/src/recipes.ts +++ b/packages/gatsby-cli/src/recipes.ts @@ -1,9 +1,11 @@ import { trackCli } from "gatsby-telemetry" -import runRecipe, { startGraphQLServer } from "gatsby-recipes" +import { startGraphQLServer, recipesHandler as runRecipe } from "gatsby-recipes" export async function recipesHandler( projectRoot: string, - recipe: string | undefined + recipe: string | undefined, + develop: boolean, + install: boolean ): Promise { trackCli(`RECIPE_RUN`, { name: recipe }) @@ -11,6 +13,8 @@ export async function recipesHandler( return runRecipe({ recipe, + isDevelopMode: develop, + isInstallMode: install, graphqlPort: graphql.port, projectRoot, }) diff --git a/packages/gatsby-recipes/.babelrc.json b/packages/gatsby-recipes/.babelrc.json new file mode 100644 index 0000000000000..e4718b43d4b14 --- /dev/null +++ b/packages/gatsby-recipes/.babelrc.json @@ -0,0 +1,7 @@ +{ + "presets": [ + ["@babel/env", { "modules": false, "targets": { "node": "current" } }], + "@babel/preset-react" + ], + "plugins": ["@babel/plugin-transform-runtime"] +} diff --git a/packages/gatsby-recipes/README.md b/packages/gatsby-recipes/README.md index 30b37083df7d3..464169b0d2c42 100644 --- a/packages/gatsby-recipes/README.md +++ b/packages/gatsby-recipes/README.md @@ -241,6 +241,8 @@ DEBUG=true node --inspect-brk ./node_modules/.bin/gatsby recipes ./test.mdx Then, open up Chrome and click the node icon in dev tools. +To see log output from the Recipes graphql server, run in a terminal `node node_modules/gatsby-recipes/dist/graphql-server/start-dev-cli-server.js` + ### Official recipes MDX source for the official recipes lives at [https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-recipes/recipes](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-recipes/recipes). diff --git a/packages/gatsby-recipes/babel.config.js b/packages/gatsby-recipes/non-rollup-babel.config.js similarity index 100% rename from packages/gatsby-recipes/babel.config.js rename to packages/gatsby-recipes/non-rollup-babel.config.js diff --git a/packages/gatsby-recipes/package.json b/packages/gatsby-recipes/package.json index 2d49a99405b90..99ddd214e3a8e 100644 --- a/packages/gatsby-recipes/package.json +++ b/packages/gatsby-recipes/package.json @@ -3,36 +3,47 @@ "description": "Core functionality for Gatsby Recipes", "version": "0.1.59", "author": "Kyle Mathews ", - "main": "dist/index.js", "bugs": { "url": "https://github.com/gatsbyjs/gatsby/issues" }, "dependencies": { - "@babel/core": "^7.10.3", - "@babel/generator": "^7.10.3", - "@babel/helper-plugin-utils": "^7.10.3", - "@babel/plugin-transform-react-jsx": "^7.10.3", - "@babel/standalone": "^7.10.3", - "@babel/template": "^7.10.3", - "@babel/types": "^7.10.3", - "@graphql-tools/schema": "^6.0.11", - "@graphql-tools/utils": "^6.0.11", - "@hapi/hoek": "8.x", + "@babel/core": "^7.9.6", + "@babel/generator": "^7.9.6", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.9.4", + "@babel/plugin-transform-react-jsx": "^7.9.4", + "@babel/standalone": "^7.10.2", + "@babel/template": "^7.8.6", + "@babel/types": "^7.9.6", + "@emotion/core": "^10.0.14", + "@emotion/styled": "^10.0.14", + "@graphql-tools/schema": "^6.0.14", + "@graphql-tools/utils": "^6.0.14", + "@hapi/hoek": "8.x.x", "@hapi/joi": "^15.1.1", - "@mdx-js/mdx": "^1.6.6", - "@mdx-js/react": "^1.6.6", - "@mdx-js/runtime": "^1.6.6", - "acorn": "^7.3.1", + "@mdx-js/mdx": "^2.0.0-next.4", + "@mdx-js/react": "^2.0.0-next.4", + "@mdx-js/runtime": "^2.0.0-next.4", + "acorn": "^7.2.0", "acorn-jsx": "^5.2.0", + "ansi-html": "^0.0.7", + "babel-plugin-remove-export-keywords": "^1.6.5", + "chokidar": "3.4.0", + "concurrently": "^5.0.0", + "contentful-management": "^5.26.3", "cors": "^2.8.5", "debug": "^4.1.1", "detect-port": "^1.3.0", + "dotenv": "^8.2.0", "execa": "^4.0.2", "express": "^4.17.1", "express-graphql": "^0.9.0", + "flatted": "^3.0.0", + "formik": "^2.0.8", "fs-extra": "^8.1.0", "gatsby-core-utils": "^1.3.14", - "gatsby-telemetry": "^1.3.25", + "gatsby-interface": "^0.0.166", + "gatsby-telemetry": "^1.3.24", "glob": "^7.1.6", "graphql": "^14.6.0", "graphql-compose": "^6.3.8", @@ -40,21 +51,24 @@ "graphql-type-json": "^0.3.2", "hicat": "^0.7.0", "html-tag-names": "^1.1.5", - "ink": "^2.7.1", - "ink-box": "^1.0.0", - "ink-link": "^1.1.0", - "ink-select-input": "^3.1.2", - "ink-spinner": "^3.1.0", "is-binary-path": "^2.1.0", "is-url": "^1.2.4", + "isomorphic-fetch": "^2.1.0", "jest-diff": "^25.5.0", "lodash": "^4.17.15", "mkdirp": "^0.5.1", - "node-fetch": "^2.6.0", + "node-fetch": "^2.5.0", + "normalize.css": "^8.0.1", + "p-queue": "^6.4.0", "pkg-dir": "^4.2.0", "prettier": "^2.0.5", + "prop-types": "^15.6.1", + "property-information": "5.5.0", + "react-circular-progressbar": "^2.0.0", + "react-icons": "^3.0.1", "react-reconciler": "^0.25.1", - "remark-mdx": "^1.6.6", + "remark-mdx": "^2.0.0-next.4", + "remark-mdxjs": "^2.0.0-next.4", "remark-parse": "^6.0.3", "remark-stringify": "^8.1.0", "resolve-cwd": "^3.0.0", @@ -65,50 +79,73 @@ "subscriptions-transport-ws": "^0.9.16", "svg-tag-names": "^2.0.1", "unified": "^8.4.2", + "unist-util-remove": "^2.0.0", "unist-util-visit": "^2.0.2", - "urql": "^1.9.8", + "urql": "^1.9.7", + "uuid": "^8.2.0", "ws": "^7.3.0", - "xstate": "^4.11.0" - }, - "peerDependencies": { - "react": "^16.12.0" + "xstate": "^4.9.1", + "yoga-layout-prebuilt": "^1.9.6", + "yup": "^0.27.0" }, "devDependencies": { "@babel/cli": "^7.10.3", + "@babel/plugin-transform-runtime": "^7.0.0", + "@rollup/plugin-babel": "^5.1.0", + "@rollup/plugin-commonjs": "^14.0.0", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^8.4.0", + "ink": "next", + "ink-select-input": "^4.0.0", + "ink-spinner": "^4.0.0-0", "react": "^16.12.0", "rimraf": "^3.0.2", + "rollup": "^2.23.0", + "rollup-plugin-auto-external": "^2.0.0", + "rollup-plugin-internal": "^1.0.0", + "terminal-link": "^2.0.0", "tmp-promise": "^2.1.0" }, "homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-recipes#readme", + "jest": { + "testPathIgnorePatterns": [ + "/.cache/", + "dist" + ], + "testEnvironment": "node" + }, "keywords": [ "gatsby", "gatsby-recipes", "mdx" ], "license": "MIT", + "main": "dist/index.js", "repository": { "type": "git", "url": "https://github.com/gatsbyjs/gatsby.git", "directory": "packages/gatsby-recipes" }, "resolutions": { - "graphql": "^14.6.0" - }, - "jest": { - "testPathIgnorePatterns": [ - "/.cache/", - "dist" - ], - "testEnvironment": "node" + "@mdx-js/mdx": "^2.0.0-next.4", + "@mdx-js/react": "^2.0.0-next.4", + "@mdx-js/runtime": "^2.0.0-next.4", + "graphql": "^14.6.0", + "property-information": "5.5.0", + "remark-mdx": "^2.0.0-next.4", + "remark-mdxjs": "^2.0.0-next.4" }, "scripts": { - "build": "babel src --out-dir dist --ignore \"**/__tests__\" --extensions \".ts,.js,.tsx\"", - "build:watch": "npm run build -- --watch", + "build": "concurrently \"npm run build:babel\" \"npm run build:rollup\"", + "build:babel": "babel --config-file ./non-rollup-babel.config.js src --out-dir dist --ignore \"**/__tests__\" --extensions \".ts,.js,.tsx\"", + "build:rollup": "rollup -c", + "build:watch": "npm run build:babel -- --watch", "prebuild": "rimraf dist", "prepare": "npm run build", "prewatch": "rimraf dist", - "watch": "npm run build:watch", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "watch": "concurrently \"npm run build:watch\" \"npm run watch:rollup\"", + "watch:rollup": "rollup -c -w" } } diff --git a/packages/gatsby-recipes/recipes/animated-page-transitions.mdx b/packages/gatsby-recipes/recipes/animated-page-transitions.mdx index 0cee28993c6ea..fe655a5021729 100644 --- a/packages/gatsby-recipes/recipes/animated-page-transitions.mdx +++ b/packages/gatsby-recipes/recipes/animated-page-transitions.mdx @@ -2,22 +2,24 @@ This recipe helps you create transitions for animating between entering and exiting Gatsby pages. +This recipe: + --- -The first step is installing the NPM packages you need: +Installs these NPM packages: --- -Add the plugin to your Gatsby config. +Adds the following plugin to your Gatsby config: --- -Now let's create a few example pages to animate between: +Creates a few example pages to animate between: @@ -28,5 +30,3 @@ Now let's create a few example pages to animate between: When you run your site you can navigate to http://localhost:8000/transition-paint-drip to try it out. See more examples about usage in the docs for the transition link plugin: https://transitionlink.tylerbarnes.ca/docs/ - -And your recipe is served! diff --git a/packages/gatsby-recipes/recipes/ava.mdx b/packages/gatsby-recipes/recipes/ava.mdx index 059ae32223e0b..71b86eca47cd0 100644 --- a/packages/gatsby-recipes/recipes/ava.mdx +++ b/packages/gatsby-recipes/recipes/ava.mdx @@ -1,16 +1,18 @@ # Add AVA -This recipe helps you setup AVA in your Gatsby site to test components and utilities +This recipe helps you setup AVA in your Gatsby site to test components and utilities. + +This recipe: --- -Install the `ava` package +Installs the `ava` package. --- -Adding some AVA test files for you to play with +Adds some AVA test files for you to play with. --- -Well look at that — we've added dependencies to your package.json and we also installed a useful package `gatsby-cypress`. `gatsby-cypress` exposes additional Cypress functionality which makes Gatsby and Cypress work together just a bit more nicely. We'll show that later with our first test, but hold tight for just a bit because first we need to scaffold out some boilerplate files for Cypress. - ---- +Creates a local `cypress` folder with two sub-folders, `support` and `plugins`. It automatically includes all the `gatsby-cypress` utilities, which you can use in your first test. @@ -12,15 +16,13 @@ Install necessary NPM packages --- -Install the Emotion plugin in gatsby-config.js +Installs the Emotion plugin in gatsby-config.js --- -Sweet, now it's ready to go. - -Let's also write out an example page you can use to play +Writes out an example page you can use to play with Emotion. --- -Install `gatsby-plugin-eslint` +Installs `gatsby-plugin-eslint`. --- -Write out a basic .eslintrc.js file that you can extend with your own chosen +Writes out a basic .eslintrc.js file that you can extend with your own chosen eslint plugins and presets. --- -Install the Layout plugin in gatsby-config.js +Installs the Layout plugin in gatsby-config.js --- -Sweet, now it's ready to go! - -Let's also write out a sample layout component to get started with. +Writes out a sample layout component to get started with. --- -Install the React Helmet plugin in gatsby-config.js +Installs the React Helmet plugin in `gatsby-config.js`. --- -Great, now it's ready to go! - -It's also common to have a `` component which helps ensure pages have the necessary title and meta tags. - -We'll write out one now. +Writes out an `` component which helps ensure pages have the necessary title and meta tags. --- -Install the gatsby-theme-blog-core plugin in gatsby-config.js. Make use of the `basePath` configuration so the blog listing appears at `/blog`. +Installs the `gatsby-theme-blog-core` plugin in `gatsby-config.js` and makes use of the `basePath` configuration so the blog listing appears at `/blog`. --- -Now let's add some posts! + +Adds some posts. --- -You're now set up to style your blog however you like! +After installing this recipe, you will be setup to style your blog however you like. -Run `gatsby develop` and check out `http://localhost:8000/blog` for the list of posts. +When you've finished installing the recipe, run `gatsby develop` and check out `http://localhost:8000/blog` for the list of posts. Example content is available at `http://localhost:8000/blog/hello-world` and `http://localhost:8000/blog/second-post`. diff --git a/packages/gatsby-recipes/recipes/gatsby-theme-blog.mdx b/packages/gatsby-recipes/recipes/gatsby-theme-blog.mdx index 367daa7b6227f..e8bd0233e5167 100644 --- a/packages/gatsby-recipes/recipes/gatsby-theme-blog.mdx +++ b/packages/gatsby-recipes/recipes/gatsby-theme-blog.mdx @@ -2,29 +2,30 @@ [Gatsby theme blog](https://www.gatsbyjs.org/packages/gatsby-theme-blog/) is a great theme for adding blog functionality to your site. +This recipe: + --- -Install necessary NPM packages +Installs necessary NPM packages. --- -Install the gatsby-theme-blog plugin in gatsby-config.js. Make use of the `basePath` configuration so the blog listing appears at `/blog`. +Installs the `gatsby-theme-blog` plugin in `gatsby-config.js` and makes use of the `basePath` configuration so the blog listing appears at `/blog`. --- -Now let's add a post! + +Adds a post. --- -And just like that, you're ready to start blogging! - -Run `gatsby develop` and check out `http://localhost:8000/blog` for the list of posts. +When you've finished installing the recipe, run `gatsby develop` and check out `http://localhost:8000/blog` for the list of posts. Example content is available at `http://localhost:8000/blog/first-blog`. diff --git a/packages/gatsby-recipes/recipes/gatsby-theme-notes.mdx b/packages/gatsby-recipes/recipes/gatsby-theme-notes.mdx index daf681ee74d2c..c1e77d942c250 100644 --- a/packages/gatsby-recipes/recipes/gatsby-theme-notes.mdx +++ b/packages/gatsby-recipes/recipes/gatsby-theme-notes.mdx @@ -2,28 +2,31 @@ [Gatsby theme notes](https://www.gatsbyjs.org/packages/gatsby-theme-notes) is a great theme for adding learning notes to your site. +This recipe: + --- -Install necessary NPM package +Installs necessary NPM package. --- -Install the gatsby-theme-notes plugin in gatsby-config.js +Installs the `gatsby-theme-notes` plugin in `gatsby-config.js`. --- -Now let's add a note! + +Adds a note. --- -Now you're ready to use your site for notetaking. +After installing this recipe, you can use your site for notetaking. -Run `gatsby develop` and check out `http://localhost:8000/notes` for the list of notes. +When you've finished installing the recipe, run `gatsby develop` and check out `http://localhost:8000/notes` for the list of notes. --- diff --git a/packages/gatsby-recipes/recipes/gitlab-ci-cd.mdx b/packages/gatsby-recipes/recipes/gitlab-ci-cd.mdx index 3144986ffe22e..b051195a5dff0 100644 --- a/packages/gatsby-recipes/recipes/gitlab-ci-cd.mdx +++ b/packages/gatsby-recipes/recipes/gitlab-ci-cd.mdx @@ -1,10 +1,12 @@ # Add GitLab CI/CD -This recipe helps you setup GitLab CI/CD in your Gatsby site to create a pipeline on gitlab.com +This recipe helps you setup GitLab CI/CD in your Gatsby site to create a pipeline on gitlab.com. + +This recipe: --- -Adding the `.gitlab-ci.yml` +Adds the `.gitlab-ci.yml`. ` you will have your gatsby project building! +After installing this recipe, every time you `git push ` you will have your gatsby project building. ---- +More resources: - See how you can develop this simple file into something more real world [GitLab CI/CD Docs](https://docs.gitlab.com/ee/ci/README.html) - Check this especially to learn how to make your newly build available for a next job - [GitLab Job Artifacts Docs](https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html) diff --git a/packages/gatsby-recipes/recipes/jest.mdx b/packages/gatsby-recipes/recipes/jest.mdx index 4618a6a79a2f3..c2ec3cedcf954 100644 --- a/packages/gatsby-recipes/recipes/jest.mdx +++ b/packages/gatsby-recipes/recipes/jest.mdx @@ -1,20 +1,22 @@ # Add Jest -This recipe helps you setup Jest in your Gatsby site to test components and utilities +This recipe helps you setup Jest in your Gatsby site to test components and utilities. +This recipe: + --- -Installing the `jest` package +Installs the `jest` package. --- -Adding some jest test files for you to play with +Adds some jest test files for you to play with. + +--- + +Once you've installed this recipe, try running `npm run test` and jest will run your test. And, while writing tests, you can run `npm run test:watch` and tests will re-run +as you edit them. diff --git a/packages/gatsby-recipes/recipes/mdx-images.mdx b/packages/gatsby-recipes/recipes/mdx-images.mdx index df3620af89fa1..b631cc6ef69b5 100644 --- a/packages/gatsby-recipes/recipes/mdx-images.mdx +++ b/packages/gatsby-recipes/recipes/mdx-images.mdx @@ -1,13 +1,16 @@ # Setup images in MDX with Gatsby This recipe installs and configures all -the necessary NPM packages & Gatsby Plugins +the necessary NPM packages & Gatsby plugins to enable inline-images in MDX and MD files. Based on https://www.gatsbyjs.org/docs/working-with-images-in-markdown/ +This recipe: + --- -Install NPM Packages + +Installs NPM Packages. ---- - @@ -38,9 +39,8 @@ Install NPM Packages /> --- -Install Gatsby Plugins -First, add `gatsby-source-filesystem` to read images from the src/images +Adds Gatsby plugin `gatsby-source-filesystem` to read images from the src/images directory. + --- -Finally, configure `gatsby-plugin-mdx` to resolve images in MDX files. +Configures `gatsby-plugin-mdx` to resolve images in MDX files. --- -Then we'll add an example MDX page for you to play with. +Adds an example MDX page for you to play with. @@ -14,7 +16,7 @@ Install the necessary NPM packages: --- -Add the Preact plugin to your Gatsby config. It will setup the correct configuration, which will let gatsby use Preact instead of React. +Adds the Preact plugin to your Gatsby config. It will setup the correct configuration, which will let gatsby use Preact instead of React. @@ -14,7 +16,7 @@ Install packages. --- -Implement git hooks for prettier. +Implements git hooks for `prettier`. --- -Add the manifest plugin and the offline plugin to your Gatsby config, which will add a manifest file and a service worker for you. +Adds the manifest plugin and the offline plugin to your Gatsby config, which will add a manifest file and a service worker for you. -We also add a default icon @ src/images/icon.svg (which you can replace afterwards with your own). +It also adds a default icon @ src/images/icon.svg (which you can replace afterwards with your own). --- -Install the Emotion plugin in gatsby-config.js +Installs the Emotion plugin in `gatsby-config.js`. --- -Sweet, now it's ready to go. - -Let's also write out an example stylesheet and page you can use to play +Writes out an example stylesheet and page you can use to play with Sass. @@ -19,7 +21,7 @@ Install babel plugins and presets as well as the Storybook React NPM packages an --- -Create JavaScript Storybook webpack config (main.js) +Creates JavaScript Storybook webpack config (`main.js`). @@ -23,7 +25,7 @@ Install TypesScript + babel plugins and presets as well as the Storybook React N --- -Create custom Storybook webpack config (main.js) +Creates custom Storybook webpack config (`main.js`). @@ -13,15 +15,13 @@ Install necessary NPM packages --- -Install the Styled Components plugin in gatsby-config.js +Installs the Styled Components plugin in `gatsby-config.js`. --- -Sweet, now it's ready to go. - -Let's also write out an example page you can use to play +Writes out an example page you can use to play with Styled Components. --- -Install necessary Gatsby Plugins +Installs necessary Gatsby plugins. --- -Setup necessary Files +Sets up necessary files. ---- - -You are good to go diff --git a/packages/gatsby-recipes/recipes/theme-ui.mdx b/packages/gatsby-recipes/recipes/theme-ui.mdx index a8a240f27ce52..e28d8662012ed 100644 --- a/packages/gatsby-recipes/recipes/theme-ui.mdx +++ b/packages/gatsby-recipes/recipes/theme-ui.mdx @@ -4,9 +4,11 @@ This recipe helps you start developing with the [Theme UI](https://theme-ui.com) +This recipe: + --- -Install packages. +Installs packages. @@ -14,13 +16,13 @@ Install packages. --- -Add the plugin `gatsby-plugin-theme-ui` to your `gatsby-config.js`. +Adds the plugin `gatsby-plugin-theme-ui` to your `gatsby-config.js`. --- -Write out Theme UI configuration files. +Writes out Theme UI configuration files. @@ -13,18 +15,16 @@ Install necessary NPM packages --- -Install the plugin `gatsby-plugin-typescript` in your `gatsby-config.js`. +Installs the plugin `gatsby-plugin-typescript` in your `gatsby-config.js`. --- -Add a tsconfig.json file to control how TypeScript processes your code. +Adds a `tsconfig.json` file to control how TypeScript processes your code. --- -TypeScript is now setup! - -You can now add TypeScript code, components, and pages in your sites `src` directory. +After installing this recipe, you can add TypeScript code, components, and pages in your sites `src` directory. diff --git a/packages/gatsby-recipes/recipes/wordpress.mdx b/packages/gatsby-recipes/recipes/wordpress.mdx index 1b229f260e55c..2ba0c19a46607 100644 --- a/packages/gatsby-recipes/recipes/wordpress.mdx +++ b/packages/gatsby-recipes/recipes/wordpress.mdx @@ -1,16 +1,18 @@ # Setup Gatsby with WordPress REST API -This recipes setups and configures gatsby-source-wordpress and starter page templates for a WordPress-powered Gatsby site. +This recipes sets up and configures `gatsby-source-wordpress` and starter page templates for a WordPress-powered Gatsby site. + +This recipe: --- -Install necessary NPM packages +Installs necessary NPM packages. --- -Install `gatsby-source-wordpress` to pull the WordPress data into Gatsby. Note, once this recipe finishes, you will need to update the config to point to your actual WordPress URL +Installs `gatsby-source-wordpress` to pull the WordPress data into Gatsby. Note, once this recipe finishes, you will need to update the config to point to your actual WordPress URL. --- -Create basic post/page/category/tag/user templates +Creates basic post, page, category, tag, and user templates. { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array) - } -} - -const applyPlan = async stepPlan => { - let appliedResources = [] - // We apply each resource serially for now — we can parallelize in the - // future for SPEED - await asyncForEach(stepPlan, async resourcePlan => { - const resource = resources[resourcePlan.resourceName] - - try { - const changedResources = await resource.create( - ctx, - resourcePlan.resourceDefinitions - ) +module.exports = async (context, cb) => { + const stepAsMdx = [...context.steps, ...context.exports].join(`\n`) - appliedResources = appliedResources.concat(changedResources) - - return - } catch (e) { - throw e - } - }) - - return appliedResources + try { + const result = await render(stepAsMdx, cb, context.inputs, true) + return result + } catch (e) { + console.log(e) + throw e + } } - -module.exports = applyPlan diff --git a/packages/gatsby-recipes/src/cli.js b/packages/gatsby-recipes/src/cli/index.js similarity index 53% rename from packages/gatsby-recipes/src/cli.js rename to packages/gatsby-recipes/src/cli/index.js index 66da0cfdc6e9e..8d60e3aa4d8d7 100644 --- a/packages/gatsby-recipes/src/cli.js +++ b/packages/gatsby-recipes/src/cli/index.js @@ -1,14 +1,18 @@ -const fs = require(`fs`) -const lodash = require(`lodash`) -const Boxen = require(`ink-box`) const React = require(`react`) -const { useState } = require(`react`) -const { render, Box, Text, Color, useInput, useApp, Static } = require(`ink`) -const Spinner = require(`ink-spinner`).default -const Link = require(`ink-link`) -const MDX = require(`@mdx-js/runtime`) +const { useState, useEffect } = require(`react`) +import SelectInput from "ink-select-input" +import { render, Box, Text, useInput, useApp, Transform } from "ink" +import Spinner from "ink-spinner" +import MDX from "../components/mdx" const hicat = require(`hicat`) -import { trackCli } from "gatsby-telemetry" +const { trackCli } = require(`gatsby-telemetry`) +import { + useResource, + useResourceByUUID, + ResourceProvider, +} from "../renderer/resource-provider" +import terminalLink from "terminal-link" +import PropTypes from "prop-types" const { createClient, useMutation, @@ -20,18 +24,38 @@ const { const { SubscriptionClient } = require(`subscriptions-transport-ws`) const fetch = require(`node-fetch`) const ws = require(`ws`) -const SelectInput = require(`ink-select-input`).default const semver = require(`semver`) +const remove = require(`unist-util-remove`) -const MAX_UI_WIDTH = 67 +const removeJsx = () => tree => { + remove(tree, `export`, () => true) + return tree +} -// TODO try this and write out success stuff & last message? -// const enterAltScreenCommand = "\x1b[?1049h" -// const leaveAltScreenCommand = "\x1b[?1049l" -// process.stdout.write(enterAltScreenCommand) -// process.on("exit", () => { -// process.stdout.write(leaveAltScreenCommand) -// }) +// Inline ink-link as it's not upgraded to v3 yet +const Link = props => ( + + terminalLink(children, props.url, { fallback: props.fallback }) + } + > + {props.children} + +) + +Link.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, + url: PropTypes.string, + fallback: PropTypes.bool, +} + +Link.defaultProps = { + url: ``, + fallback: true, +} // Check for what version of React is loaded & warn if it's too low. if (semver.lt(React.version, `16.8.0`)) { @@ -42,19 +66,24 @@ if (semver.lt(React.version, `16.8.0`)) { const WelcomeMessage = () => ( <> - - Thank you for trying the experimental version of Gatsby Recipes! - + + Thank you for trying the experimental version of Gatsby Recipes! + +
- Please ask questions, share your recipes, report bugs, and subscribe for - updates in our umbrella issue at - https://github.com/gatsbyjs/gatsby/issues/22991 + + Please ask questions, share your recipes, report bugs, and subscribe for + updates in our umbrella issue at + https://github.com/gatsbyjs/gatsby/issues/22991 +
) @@ -170,36 +199,29 @@ const RecipesList = ({ setRecipe }) => { ] return ( - ( - + {item.isSelected ? `>>` : ` `} {item.label} - + )} itemComponent={props => ( - {props.label} + + {props.isSelected} + {` `} + {props.label} + )} /> ) } -let renderCount = 1 - -const Div = props => { - const width = Math.min(process.stdout.columns, MAX_UI_WIDTH) - return ( - - ) -} +const Div = props => ( + +) // Markdown ignores new lines and so do we. function eliminateNewLines(children) { @@ -218,8 +240,33 @@ function eliminateNewLines(children) { }) } +const ResourceComponent = props => { + let resource + if (props._key) { + resource = useResource(props._key) + } else { + resource = useResourceByUUID(props._uuid) + } + + return ( +
+ + {resource.resourceName}: + + {resource?.describe} + {resource?.diff ? ( + <> + {` `} + {resource?.diff}" + + ) : null} +
+ ) +} + const components = { - inlineCode: props => , + inlineCode: props => `{props.children}`, + pre: props =>
, code: props => { // eslint-disable-next-line let language = "```" @@ -238,75 +285,78 @@ const components = { ) }, h1: props => ( -
+ -
- ), - h2: props => ( -
- -
- ), - h3: props => ( -
- -
- ), - h4: props => ( -
- -
- ), - h5: props => ( -
- -
- ), - h6: props => ( -
- -
+ ), + h2: props => , + h3: props => , + h4: props => , + h5: props => , + h6: props => , a: ({ href, children }) => {children}, strong: props => , em: props => , p: props => { const children = eliminateNewLines(props.children) return ( -
+
{children}
) }, + // Don't use for li > p as that breaks Ink. + "li.p": props => { + const children = eliminateNewLines(props.children) + return {children} + }, + // p: () => `hi`, // null, ul: props =>
{props.children}
, li: props => * {props.children}, Config: () => null, - GatsbyPlugin: () => null, - NPMPackageJson: () => null, - NPMPackage: () => null, - File: () => null, - Directory: () => null, + GatsbyPlugin: props => , + NPMPackageJson: props => , + NPMPackage: props => , + File: props => , + Directory: props => , GatsbyShadowFile: () => null, - NPMScript: () => null, -} + NPMScript: props => , + RecipeIntroduction: props =>
, + RecipeStep: props => { + const children = React.Children.toArray(props.children) + const firstChild = children.shift() + children.unshift( + + + {props.step}){` `} + + {firstChild} + + ) -let logStream -const log = (label, textOrObj) => { - if (process.env.DEBUG) { - logStream = - logStream ?? fs.createWriteStream(`recipe-client.log`, { flags: `a` }) - logStream.write(`[${label}]:\n`) - logStream.write(require(`util`).inspect(textOrObj)) - logStream.write(`\n`) - } + return ( +
+ + {children} + +
+ ) + }, + div: props =>
, } -log( - `started client`, - `======================================= ${new Date().toJSON()}` -) - -module.exports = async ({ recipe, graphqlPort, projectRoot }) => { +export default async ({ + recipe, + isDevelopMode, + isInstallMode, + graphqlPort, + projectRoot, +}) => { try { const GRAPHQL_ENDPOINT = `http://localhost:${graphqlPort}/graphql` @@ -336,7 +386,68 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { }), ], }) + const Plan = ({ state, localRecipe, isDevelopMode }) => { + const { exit } = useApp() + // Exit the app after we render + useEffect(() => { + if (!isDevelopMode) { + exit() + } + }, []) + + return ( + <> + p.resourceName !== `Input`) || [] + } + > + + {isDevelopMode ? ( + + + DEVELOP MODE + + + ) : null} + + {state.context.exports?.join(`\n`) + + `\n\n` + + state.context.steps.join(`\n`)} + + {`\n------\n`} + To install this recipe, run: + {` `} + + {` `}gatsby recipes {localRecipe} --install + + {` `} + + + ) + } + + const Installing = ({ state }) => ( +
+ {state.context.plan.map((p, i) => ( +
+ {p.resourceName}: + + {` `} + {p.isDone ? `✅ ` : } + {` `} + {p.isDone ? p._message : p.describe} + {` `} + {state.context.elapsed > 0 && ( + ({state.context.elapsed / 1000}s elapsed) + )} + +
+ ))} +
+ ) + let sentContinue = false const RecipeInterpreter = () => { const { exit } = useApp() // eslint-disable-next-line @@ -362,24 +473,36 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { } `) // eslint-disable-next-line - const [__, sendEvent] = useMutation(` - mutation($event: String!) { - sendEvent(event: $event) + const [__, _sendEvent] = useMutation(` + mutation($event: String!, $input: String) { + sendEvent(event: $event, input: $input) } `) + const sendEvent = ({ event, input }) => { + if (input) { + _sendEvent({ + event, + input: JSON.stringify(input), + }) + } else { + _sendEvent({ event }) + } + } + subscriptionClient.connectionCallback = async () => { if (!showRecipesList) { - log(`createOperation`) try { - await createOperation({ recipePath: localRecipe, projectRoot }) + await createOperation({ + recipePath: localRecipe, + projectRoot, + }) } catch (e) { - log(`error creating operation`, e) + console.log(`error creating operation`, e) } } } - log(`subscriptionResponse`, subscriptionResponse) const state = subscriptionResponse.data && JSON.parse(subscriptionResponse.data.operation.state) @@ -393,8 +516,6 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { } }) - log(`subscriptionResponse.data`, subscriptionResponse.data) - if (showRecipesList) { return ( <> @@ -404,6 +525,7 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { { + setRecipe(recipeItem.value.slice(0, -4)) trackCli(`RECIPE_RUN`, { name: recipeItem.value }) showRecipesList = false try { @@ -412,7 +534,7 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { projectRoot, }) } catch (e) { - log(`error creating operation`, e) + console.log(`error creating operation`, e) } }} /> @@ -420,140 +542,50 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { ) } - if (!state) { - return ( - - Loading recipe - - ) - } - /* - * TODOs - * Listen to "y" to continue (in addition to enter) - */ - - log(`render`, `${renderCount} ${new Date().toJSON()}`) - renderCount += 1 - - const isDone = state.value === `done` + const Error = ({ state }) => { + if (state && state.context && state.context.error) { + return {JSON.stringify(state.context.error, null, 2)} + } - // If we're done with an error, render out error (happens below) - // then exit. - if (state.value === `doneError`) { - process.nextTick(() => process.exit()) + return null } - if (process.env.DEBUG) { - log(`state`, state) - log(`plan`, state.context.plan) - log(`stepResources`, state.context.stepResources) + if (state?.value === `doneError`) { + return } - const PresentStep = ({ state }) => { - const isPlan = state.context.plan && state.context.plan.length > 0 - const isPresetPlanState = state.value === `presentPlan` - const isRunningStep = state.value === `applyingPlan` - - if (isRunningStep) { - return null - } + let isReady - if (!isPlan || !isPresetPlanState) { - return ( -
- >> Press enter to continue -
- ) + // If installing, continue from presentPlan to applyingPlan + if (state?.value === `presentPlan` && isInstallMode) { + if (!sentContinue) { + sendEvent({ event: `CONTINUE` }) + sentContinue = true } - - return ( -
-
- - Proposed changes - -
- {state.context.plan.map((p, i) => ( -
- {p.resourceName}: - * {p.describe} - {p.diff && p.diff !== `` && ( - <> - --- - {p.diff} - --- - - )} -
- ))} -
- >> Press enter to run this step -
-
- ) } - const RunningStep = ({ state }) => { - const isPlan = state.context.plan && state.context.plan.length > 0 - const isRunningStep = state.value === `applyingPlan` - - if (!isPlan || !isRunningStep) { - return null - } + // install mode + if (isInstallMode) { + isReady = state?.value === `applyingPlan` || state?.value === `done` + } else { + isReady = state?.value === `presentPlan` + } + if (!isReady) { return ( -
- {state.context.plan.map((p, i) => ( -
- {p.resourceName}: - - {` `} - {p.describe} - {` `} - {state.context.elapsed > 0 && ( - ({state.context.elapsed / 1000}s elapsed) - )} - -
- ))} -
+ + Loading recipe + ) } - const Error = ({ state }) => { - log(`errors`, state) - if (state && state.context && state.context.error) { - return ( - {JSON.stringify(state.context.error, null, 2)} - ) - } - - return null - } + const isDone = state.value === `done` + // If we're done with an error, render out error (happens below) + // then exit. if (state.value === `doneError`) { - return - } - - const staticMessages = {} - for (let step = 0; step < state.context.currentStep; step++) { - staticMessages[step] = [ - { - type: `mdx`, - key: `mdx-${step}`, - value: state.context.steps[step], - }, - ] + process.nextTick(() => process.exit()) } - lodash.flattenDeep(state.context.stepResources).forEach((res, i) => { - staticMessages[res._currentStep].push({ - type: `resource`, - key: `finished-stuff-${i}`, - value: res._message, - }) - }) - - log(`staticMessages`, staticMessages) if (isDone) { process.nextTick(() => { @@ -562,53 +594,22 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { process.stdout.write( `\n\n---\n\n\nThe recipe finished successfully!\n\n` ) - lodash.flattenDeep(state.context.stepResources).forEach((res, i) => { - process.stdout.write(`✅ ${res._message}\n`) - }) process.exit() }) // return null } - return ( - <> -
- - {Object.keys(staticMessages).map((key, iStep) => - staticMessages[key].map((r, i) => { - if (r.type && r.type === `mdx`) { - return ( -
- {iStep === 0 && Completed Steps} -
- {iStep !== 0 && `---`} -
- {iStep !== 0 && Step {iStep}} - {r.value} -
- ) - } - return ✅ {r.value} - }) - )} -
-
- {state.context.currentStep === 0 && } - {state.context.currentStep > 0 && !isDone && ( -
- - Step {state.context.currentStep} /{` `} - {state.context.steps.length - 1} - -
- )} - - {state.context.steps[state.context.currentStep]} - - {!isDone && } - {!isDone && } - - ) + if (isInstallMode) { + return + } else { + return ( + + ) + } } const Wrapper = () => ( @@ -626,6 +627,6 @@ module.exports = async ({ recipe, graphqlPort, projectRoot }) => { const { waitUntilExit } = render(, { experimental: true }) await waitUntilExit() } catch (e) { - log(e) + console.log(e) } } diff --git a/packages/gatsby-recipes/src/components/mdx.js b/packages/gatsby-recipes/src/components/mdx.js new file mode 100644 index 0000000000000..1eeabfe5be9c5 --- /dev/null +++ b/packages/gatsby-recipes/src/components/mdx.js @@ -0,0 +1,35 @@ +import React from "react" +import { mdx as createElement, MDXProvider } from "@mdx-js/react" +import { useInput, useInputByKey } from "../renderer/input-provider" +import { useResource } from "../renderer/resource-provider" +import { useProvider } from "../renderer/provider-provider" +import transformRecipeMDX from "../transform-recipe-mdx" + +const transformCodeForEval = jsx => `${jsx} + + return React.createElement(MDXProvider, { components }, + React.createElement(MDXContent, props) + );` + +export default ({ children: mdxSrc, scope, components, ...props }) => { + const fullScope = { + mdx: createElement, + MDXProvider, + React, + components, + props, + useInput, + useInputByKey, + useResource, + useProvider, + ...scope, + } + const scopeKeys = Object.keys(fullScope) + const scopeValues = Object.values(fullScope) + + const srcCode = transformRecipeMDX(mdxSrc) + + const fn = new Function(...scopeKeys, transformCodeForEval(srcCode)) + + return fn(...scopeValues) +} diff --git a/packages/gatsby-recipes/src/create-plan.js b/packages/gatsby-recipes/src/create-plan.js index 39d7815197fe5..7bfa2f74e2158 100644 --- a/packages/gatsby-recipes/src/create-plan.js +++ b/packages/gatsby-recipes/src/create-plan.js @@ -1,14 +1,10 @@ const render = require(`./renderer`) -// TODO: Properly handle context in the renderer -// const SITE_ROOT = process.cwd() -// const ctx = { root: SITE_ROOT } - -module.exports = async context => { - const stepAsMdx = context.steps[context.currentStep] +module.exports = async (context, cb) => { + const stepAsMdx = [...context.steps, ...context.exports].join(`\n`) try { - const result = await render(stepAsMdx) + const result = await render(stepAsMdx, cb, context.inputs) return result } catch (e) { throw e diff --git a/packages/gatsby-recipes/src/create-types.test.js b/packages/gatsby-recipes/src/create-types.test.js index 54df14f67e465..8b2e484acac5b 100644 --- a/packages/gatsby-recipes/src/create-types.test.js +++ b/packages/gatsby-recipes/src/create-types.test.js @@ -4,6 +4,42 @@ test(`create-types`, () => { const result = createTypes() expect(result.mutationTypes).toMatchInlineSnapshot(` Object { + "createContentfulEntry": Object { + "args": Object { + "contentfulEntry": Object { + "type": "ContentfulEntryInput", + }, + }, + "resolve": [Function], + "type": "ContentfulEntry", + }, + "createContentfulEnvironment": Object { + "args": Object { + "contentfulEnvironment": Object { + "type": "ContentfulEnvironmentInput", + }, + }, + "resolve": [Function], + "type": "ContentfulEnvironment", + }, + "createContentfulSpace": Object { + "args": Object { + "contentfulSpace": Object { + "type": "ContentfulSpaceInput", + }, + }, + "resolve": [Function], + "type": "ContentfulSpace", + }, + "createContentfulType": Object { + "args": Object { + "contentfulType": Object { + "type": "ContentfulTypeInput", + }, + }, + "resolve": [Function], + "type": "ContentfulType", + }, "createDirectory": Object { "args": Object { "directory": Object { @@ -85,6 +121,42 @@ test(`create-types`, () => { "resolve": [Function], "type": "NPMScript", }, + "destroyContentfulEntry": Object { + "args": Object { + "contentfulEntry": Object { + "type": "ContentfulEntryInput", + }, + }, + "resolve": [Function], + "type": "ContentfulEntry", + }, + "destroyContentfulEnvironment": Object { + "args": Object { + "contentfulEnvironment": Object { + "type": "ContentfulEnvironmentInput", + }, + }, + "resolve": [Function], + "type": "ContentfulEnvironment", + }, + "destroyContentfulSpace": Object { + "args": Object { + "contentfulSpace": Object { + "type": "ContentfulSpaceInput", + }, + }, + "resolve": [Function], + "type": "ContentfulSpace", + }, + "destroyContentfulType": Object { + "args": Object { + "contentfulType": Object { + "type": "ContentfulTypeInput", + }, + }, + "resolve": [Function], + "type": "ContentfulType", + }, "destroyDirectory": Object { "args": Object { "directory": Object { @@ -166,6 +238,42 @@ test(`create-types`, () => { "resolve": [Function], "type": "NPMScript", }, + "updateContentfulEntry": Object { + "args": Object { + "contentfulEntry": Object { + "type": "ContentfulEntryInput", + }, + }, + "resolve": [Function], + "type": "ContentfulEntry", + }, + "updateContentfulEnvironment": Object { + "args": Object { + "contentfulEnvironment": Object { + "type": "ContentfulEnvironmentInput", + }, + }, + "resolve": [Function], + "type": "ContentfulEnvironment", + }, + "updateContentfulSpace": Object { + "args": Object { + "contentfulSpace": Object { + "type": "ContentfulSpaceInput", + }, + }, + "resolve": [Function], + "type": "ContentfulSpace", + }, + "updateContentfulType": Object { + "args": Object { + "contentfulType": Object { + "type": "ContentfulTypeInput", + }, + }, + "resolve": [Function], + "type": "ContentfulType", + }, "updateDirectory": Object { "args": Object { "directory": Object { @@ -251,6 +359,22 @@ test(`create-types`, () => { `) expect(result.queryTypes).toMatchInlineSnapshot(` Object { + "allContentfulEntry": Object { + "resolve": [Function], + "type": "ContentfulEntryConnection", + }, + "allContentfulEnvironment": Object { + "resolve": [Function], + "type": "ContentfulEnvironmentConnection", + }, + "allContentfulSpace": Object { + "resolve": [Function], + "type": "ContentfulSpaceConnection", + }, + "allContentfulType": Object { + "resolve": [Function], + "type": "ContentfulTypeConnection", + }, "allGatsbyPage": Object { "resolve": [Function], "type": "GatsbyPageConnection", @@ -271,6 +395,42 @@ test(`create-types`, () => { "resolve": [Function], "type": "NPMScriptConnection", }, + "contentfulEntry": Object { + "args": Object { + "id": Object { + "type": "String", + }, + }, + "resolve": [Function], + "type": "ContentfulEntry", + }, + "contentfulEnvironment": Object { + "args": Object { + "id": Object { + "type": "String", + }, + }, + "resolve": [Function], + "type": "ContentfulEnvironment", + }, + "contentfulSpace": Object { + "args": Object { + "id": Object { + "type": "String", + }, + }, + "resolve": [Function], + "type": "ContentfulSpace", + }, + "contentfulType": Object { + "args": Object { + "id": Object { + "type": "String", + }, + }, + "resolve": [Function], + "type": "ContentfulType", + }, "directory": Object { "args": Object { "id": Object { diff --git a/packages/gatsby-recipes/src/graphql-server/index.js b/packages/gatsby-recipes/src/graphql-server/index.js index 3e0ff375b36aa..452c33066a909 100644 --- a/packages/gatsby-recipes/src/graphql-server/index.js +++ b/packages/gatsby-recipes/src/graphql-server/index.js @@ -10,7 +10,7 @@ const { // NOTE(@mxstbr): The forceStart boolean enforces us to start the recipes graphql server // even if another instance might already be running. This is necessary to ensure the gatsby // develop command does not _not_ run the server if the user is running gatsby recipes at the same time. -exports.startGraphQLServer = async (programPath, forceStart) => { +export default async (programPath, forceStart) => { let { port } = (await getService(programPath, `recipesgraphqlserver`)) || {} if (!port || forceStart) { @@ -19,15 +19,19 @@ exports.startGraphQLServer = async (programPath, forceStart) => { port = await detectPort(50400) await createServiceLock(programPath, `recipesgraphqlserver`, { port }) - const subprocess = execa(`node`, [require.resolve(`./server.js`), port], { - all: true, - env: { - // Chalk doesn't want to output color in a child process - // as it (correctly) thinks it's not in a normal terminal environemnt. - // Since we're just returning data, we'll override that. - FORCE_COLOR: `true`, - }, - }) + const subprocess = execa( + `node`, + [require.resolve(`gatsby-recipes/dist/graphql-server/server.js`), port], + { + all: true, + env: { + // Chalk doesn't want to output color in a child process + // as it (correctly) thinks it's not in a normal terminal environemnt. + // Since we're just returning data, we'll override that. + FORCE_COLOR: `true`, + }, + } + ) // eslint-disable-next-line no-unused-expressions subprocess.stderr?.on(`data`, data => { diff --git a/packages/gatsby-recipes/src/graphql-server/server.js b/packages/gatsby-recipes/src/graphql-server/server.js index c9e32cb63d367..244b38521b602 100644 --- a/packages/gatsby-recipes/src/graphql-server/server.js +++ b/packages/gatsby-recipes/src/graphql-server/server.js @@ -1,5 +1,9 @@ +require(`dotenv`).config() + const express = require(`express`) +const chokidar = require(`chokidar`) const graphqlHTTP = require(`express-graphql`) +const { v4: uuidv4 } = require(`uuid`) const { GraphQLSchema, GraphQLObjectType, @@ -13,6 +17,11 @@ const { createServer } = require(`http`) const { interpret } = require(`xstate`) const pkgDir = require(`pkg-dir`) const cors = require(`cors`) +const lodash = require(`lodash`) + +// Create a session id — mostly useful to tell the client when the server +// has restarted +const sessionId = uuidv4() const recipeMachine = require(`../recipe-machine`) const createTypes = require(`../create-types`) @@ -22,55 +31,76 @@ const SITE_ROOT = pkgDir.sync(process.cwd()) const pubsub = new PubSub() const PORT = process.argv[2] || 50400 -const emitOperation = state => { - console.log(state) - pubsub.publish(`operation`, { - state: JSON.stringify(state), - }) +let lastState = {} +const emitUpdate = state => { + // eslint-disable-next-line no-unused-vars + const { lastEvent, ...cleanedState } = state + if (!lodash.isEqual(cleanedState, lastState)) { + pubsub.publish(`operation`, { + state: JSON.stringify(cleanedState), + }) + + lastState = cleanedState + } } // only one service can run at a time. let service -const applyPlan = ({ recipePath, projectRoot }) => { +const startRecipe = ({ recipePath, projectRoot }) => { const initialState = { context: { recipePath, projectRoot, steps: [], currentStep: 0 }, value: `init`, } - // Interpret the machine, and add a listener for whenever a transition occurs. - service = interpret( - recipeMachine.withContext(initialState.context) - ).onTransition(state => { - // Don't emit again unless there's a state change. - console.log(`===onTransition`, { - event: state.event, - state: state.value, - context: state.context, - stepResources: state.context.stepResources, - plan: state.context.plan, - }) - if (state.changed) { - console.log(`===state.changed`, { + const startService = () => { + // Interpret the machine, and add a listener for whenever a transition occurs. + service = interpret( + recipeMachine.withContext(initialState.context) + ).onTransition(state => { + // Don't emit again unless there's a state change. + console.log(`===onTransition`, { + event: state.event, state: state.value, - currentStep: state.context.currentStep, }) - // Wait until plans are created before updating the UI - if (state.value !== `creatingPlan`) { - emitOperation({ - context: state.context, - lastEvent: state.event, - value: state.value, + if (state.changed) { + console.log(`===state.changed`, { + state: state.value, + currentStep: state.context.currentStep, }) + // Wait until plans are created before updating the UI + if ( + [`presentPlan`, `done`, `doneError`, `applyingPlan`].includes( + state.value + ) + ) { + emitUpdate({ + context: state.context, + lastEvent: state.event, + value: state.value, + }) + } } - } - }) - // Start the service - try { - service.start() - } catch (e) { - console.log(`recipe machine failed to start`, e) + if (state.value === `done`) { + service.stop() + } + }) + + // Start the service + try { + service.start() + } catch (e) { + console.log(`recipe machine failed to start`, e) + } } + + chokidar + .watch(initialState.context.recipePath) + .on(`change`, (filename, stats) => { + startService() + }) + + startService() } const OperationType = new GraphQLObjectType({ @@ -99,18 +129,22 @@ const rootMutationType = new GraphQLObjectType({ projectRoot: { type: GraphQLString }, }, resolve: (_data, args) => { - console.log(`received operation`, args.recipePath) - applyPlan(args) + console.log(`received operation`, args) + startRecipe(args) }, }, sendEvent: { type: GraphQLString, args: { event: { type: GraphQLString }, + input: { type: GraphQLString }, }, resolve: (_, args) => { - console.log(`event received`, args) - service.send(args.event) + console.log(`!!! event received`, args) + service.send({ + type: args.event, + data: args.input && JSON.parse(args.input), + }) }, }, } @@ -139,7 +173,7 @@ const schema = new GraphQLSchema({ const app = express() const server = createServer(app) -console.log(`listening on localhost:4000`) +console.log(`listening on localhost:${PORT}`) app.use(cors()) @@ -152,6 +186,15 @@ app.use( }) ) +app.use(`/session`, (req, res) => { + res.send(sessionId) +}) + +// DEBUGGING +app.use(`/service`, (req, res) => { + res.json(service) +}) + server.listen(PORT, () => { new SubscriptionServer( { diff --git a/packages/gatsby-recipes/src/graphql-server/start-dev-cli-server.js b/packages/gatsby-recipes/src/graphql-server/start-dev-cli-server.js new file mode 100644 index 0000000000000..0f49bb53a3852 --- /dev/null +++ b/packages/gatsby-recipes/src/graphql-server/start-dev-cli-server.js @@ -0,0 +1,37 @@ +const { createServiceLock } = require(`gatsby-core-utils/dist/service-lock`) +const execa = require(`execa`) + +const startServer = async () => { + // Use 50400 as our port as it's a highly composite number! Meaning it has + // more divisors than any smaller positive integer. + const port = 50400 + await createServiceLock(process.cwd(), `recipesgraphqlserver`, { port }) + + const subprocess = execa(`node`, [require.resolve(`./server.js`), port], { + all: true, + env: { + // Chalk doesn't want to output color in a child process + // as it (correctly) thinks it's not in a normal terminal environemnt. + // Since we're just returning data, we'll override that. + FORCE_COLOR: `true`, + }, + }) + + // eslint-disable-next-line no-unused-expressions + subprocess.stderr?.on(`data`, data => { + console.log(data.toString()) + }) + + // eslint-disable-next-line no-unused-expressions + subprocess.stdout?.on(`data`, data => { + console.log(data.toString()) + }) + + process.on(`exit`, () => { + subprocess.kill(`SIGTERM`, { + forceKillAfterTimeout: 2000, + }) + }) +} + +startServer() diff --git a/packages/gatsby-recipes/src/gui.js b/packages/gatsby-recipes/src/gui.js new file mode 100644 index 0000000000000..2243374eb6742 --- /dev/null +++ b/packages/gatsby-recipes/src/gui.js @@ -0,0 +1,1015 @@ +/** @jsx jsx */ +import { jsx, ThemeProvider as ThemeUIProvider, Styled } from "theme-ui" +const lodash = require(`lodash`) +// eslint-disable-next-line +const React = require(`react`) +const { useState } = require(`react`) +const ansi2HTML = require(`ansi-html`) +const remove = require(`unist-util-remove`) +const { Global } = require(`@emotion/core`) +// const ws = require(`ws`) +const fetch = require(`isomorphic-fetch`) +import { MdRefresh, MdBrightness1 } from "react-icons/md" +import { keyframes } from "@emotion/core" +import MDX from "./components/mdx" + +const { + Button, + ThemeProvider, + TextAreaField, + TextAreaFieldControl, + getTheme, + BaseAnchor, + Heading, + SuccessIcon, + InputField, + InputFieldLabel, + InputFieldControl, +} = require(`gatsby-interface`) +const { + createClient, + useMutation, + useSubscription, + Provider, + defaultExchanges, + subscriptionExchange, +} = require(`urql`) +const UrqlProvider = Provider +const { SubscriptionClient } = require(`subscriptions-transport-ws`) +const slugify = require(`slugify`) +require(`normalize.css`) + +import { InputProvider } from "./renderer/input-provider" +import { ResourceProvider } from "./renderer/resource-provider" + +const theme = getTheme() + +ansi2HTML.setColors({ + red: theme.tones.DANGER.medium.slice(1), + green: theme.tones.SUCCESS.medium.slice(1), + yellow: theme.tones.WARNING.medium.slice(1), +}) + +// const InputFieldBlock = React.forwardRef((props, ref) => { +// const { +// id, +// label, +// labelSize, +// error, +// hint, +// className, +// validationMode, +// layout, +// ...rest +// } = props + +// const hasError = false + +// return ( +// +// +// +// {label} +// +// +// {hint} +// +// {error} +// +// +// +// ) +// }) + +const makeResourceId = res => { + if (!res.describe) { + res.describe = `` + } + const id = encodeURIComponent(`${res.resourceName}-${slugify(res.describe)}`) + return id +} + +let sendEvent + +const PROJECT_ROOT = + `/Users/kylemathews/programs/recipes-test` && + `/Users/johno-mini/c/gatsby/starters/blog` + +const Spinner = () => Loading... + +const escapeTags = str => str.replace(/ ( + theme.tones.BRAND.superLight, + borderRadius: 2, + padding: 4, + }} + dangerouslySetInnerHTML={{ + __html: ansi2HTML(escapeTags(resourcePlan.diff)), + }} + /> +) + +const Diff = ({ resourcePlan, ...props }) => { + if (!resourcePlan.diff) { + return null + } + + if (resourcePlan.resourceName === `File`) { + return ( +
theme.tones.BRAND.superLight, + border: theme => `1px solid ${theme.tones.BRAND.lighter}`, + borderRadius: 2, + }} + > + `1px solid ${theme.tones.BRAND.lighter}`, + }} + > + {resourcePlan.resourceDefinitions.path} + + +
+ ) + } + + return ( + `1px solid ${theme.tones.BRAND.lighter}`, + }} + /> + ) +} + +const WelcomeMessage = () => ( +
theme.tones.BRAND.superLight, + border: theme => `1px solid ${theme.tones.BRAND.light}`, + padding: 4, + }} + > + + Thank you for trying the experimental version of Gatsby Recipes! 🤗 + + + Please ask questions, share your recipes, report bugs, and subscribe for + updates in our umbrella issue at{` `} + + https://github.com/gatsbyjs/gatsby/issues/22991 + + +
+) + +const RecipesList = ({ setRecipe }) => { + const items = [ + { + label: `Add a custom ESLint config`, + value: `eslint.mdx`, + }, + { + label: `Add Jest`, + value: `jest.mdx`, + }, + { + label: `Add Gatsby Theme Blog`, + value: `gatsby-theme-blog`, + }, + { + label: `Add persistent layout component with gatsby-plugin-layout`, + value: `gatsby-plugin-layout`, + }, + { + label: `Add Theme UI`, + value: `theme-ui.mdx`, + }, + { + label: `Add Emotion`, + value: `emotion.mdx`, + }, + { + label: `Add support for MDX Pages`, + value: `mdx-pages.mdx`, + }, + { + label: `Add support for MDX Pages with images`, + value: `mdx-images.mdx`, + }, + { + label: `Add Styled Components`, + value: `styled-components.mdx`, + }, + { + label: `Add Tailwind`, + value: `tailwindcss.mdx`, + }, + { + label: `Add Sass`, + value: `sass.mdx`, + }, + { + label: `Add Typescript`, + value: `typescript.mdx`, + }, + { + label: `Add Cypress testing`, + value: `cypress.mdx`, + }, + { + label: `Add animated page transition support`, + value: `animated-page-transitions.mdx`, + }, + { + label: `Add plugins to make site a PWA`, + value: `pwa.mdx`, + }, + { + label: `Add React Helmet`, + value: `gatsby-plugin-react-helmet.mdx`, + }, + { + label: `Add Storybook - JavaScript`, + value: `storybook-js.mdx`, + }, + { + label: `Add Storybook - TypeScript`, + value: `storybook-ts.mdx`, + }, + // TODO remaining recipes + ] + + return ( + + ) +} + +const components = { + Config: () => null, + GatsbyPlugin: () => null, + NPMPackageJson: () => null, + NPMPackage: () => null, + File: () => null, + Input: () => null, + Directory: () => null, + GatsbyShadowFile: () => null, + NPMScript: () => null, + RecipeIntroduction: props =>
{props.children}
, + RecipeStep: props =>
{props.children}
, + ContentfulSpace: () => null, + ContentfulEnvironment: () => null, + ContentfulType: () => null, +} + +const log = (label, textOrObj) => { + console.log(label, textOrObj) +} + +log( + `started client`, + `======================================= ${new Date().toJSON()}` +) + +const removeJsx = () => tree => { + remove(tree, `export`, () => true) + return tree +} + +const recipe = `styled-components.mdx` +// recipe = `jest.mdx`, +// recipe, +const graphqlPort = 50400 +const projectRoot = PROJECT_ROOT + +const API_ENDPOINT = `http://localhost:${graphqlPort}` +const GRAPHQL_ENDPOINT = `${API_ENDPOINT}/graphql` + +const subscriptionClient = new SubscriptionClient( + `ws://localhost:${graphqlPort}/graphql`, + { + reconnect: true, + } + // ws +) + +let isSubscriptionConnected = false +let isRecipeStarted = false +let sessionId +subscriptionClient.connectionCallback = async () => { + isSubscriptionConnected = true +} + +const checkServerSession = async () => { + const response = await fetch(`${API_ENDPOINT}/session`) + const newSessionId = await response.text() + if (!sessionId) { + sessionId = newSessionId + } else if (newSessionId !== sessionId) { + window.location.reload() + } +} + +let showRecipesList = false + +if (!recipe) { + showRecipesList = true +} + +const client = createClient({ + fetch, + url: GRAPHQL_ENDPOINT, + exchanges: [ + ...defaultExchanges, + subscriptionExchange({ + forwardSubscription(operation) { + return subscriptionClient.request(operation) + }, + }), + ], +}) + +const ResourcePlan = ({ resourcePlan, isLastPlan }) => ( +
+
+ + {resourcePlan.resourceName} + {` `}—{` `} + {resourcePlan.describe} + +
+ + {resourcePlan.resourceChildren + ? resourcePlan.resourceChildren.map(resource => ( + + )) + : null} +
+) + +const Step = ({ state, step, i }) => { + const stepResources = state.context?.plan?.filter( + p => parseInt(p._stepMetadata.step, 10) === i + 1 + ) + + return ( +
`1px solid ${theme.tones.BRAND.light}`, + marginBottom: 7, + }} + > +
theme.tones.BRAND.dark, + right: `6px`, + top: `6px`, + border: theme => `1px solid ${theme.tones.BRAND.light}`, + borderRadius: 9999, + height: 30, + width: 30, + display: `flex`, + alignContent: `center`, + justifyContent: `center`, + lineHeight: `28px`, + }} + > + {i + 1} +
+
*": { + marginY: 0, + }, + borderTopLeftRadius: 2, + borderTopRightRadius: 2, + borderBottom: theme => `1px solid ${theme.tones.BRAND.light}`, + background: theme => theme.tones.BRAND.lighter, + padding: 4, + }} + > +
+ + {state.context.exports?.join(`\n`) + `\n\n` + step} + +
+
+ {stepResources?.length > 0 && ( +
+
+ {stepResources?.map((res, i) => { + if (res.resourceName === `Input`) { + if (res.type === `textarea`) { + return ( +
+ +
+ {res.label} +
+ { + sendInputEvent({ + uuid: res._uuid, + key: res._key, + value: e.target.value, + }) + }} + /> +
+
+ ) + } + return ( +
+ +
+ {res.label} +
+ { + sendInputEvent({ + uuid: res._uuid, + key: res._key, + value: e.target.value, + }) + }} + /> +
+
+ ) + } + + return null + })} +
+
+ theme.tones.NEUTRAL.darker, + fontWeight: 500, + }} + as={`h3`} + > + Proposed changes + + {stepResources?.map((res, i) => { + if (res.resourceName !== `Input`) { + return ( + + ) + } + + return null + })} +
+
+ )} +
+ ) +} + +const sendInputEvent = event => { + sendEvent({ + event: `INPUT_ADDED`, + input: event, + }) +} + +const ResourceMessage = ({ state, resource }) => { + let icon = ( + + ) + let message = resource.describe + + if (state.value === `applyingPlan` && resource.isDone) { + icon = + } else if (state.value === `applyingPlan`) { + const keyframe = keyframes` + 0% { + transform: rotate(0); + } + 100% { + transform: rotate(360deg); + } +` + icon = ( + + ) + message = resource.describe + } else if (state.value === `done`) { + icon = + message = resource._message + } + + return ( + <> + {icon} + {` `} + { + e.preventDefault() + const target = document.getElementById(e.currentTarget.hash.slice(1)) + target.scrollIntoView({ + behavior: `smooth`, // smooth scroll + block: `start`, // the upper border of the element will be aligned at the top of the visible part of the window of the scrollable area. + }) + }} + > + {message} + + + ) +} + +const ButtonText = state => { + if (state.value === `done`) { + return `Refresh State` + } + + return `Install Recipe` +} + +const ResourceChildren = ({ resourceChildren, state }) => { + if (!resourceChildren || !resourceChildren.length) { + return null + } + + return ( + + {resourceChildren.map(resource => ( + + + + + ))} + + ) +} + +function Wrapper({ children }) { + return
{children}
+} + +const RecipeInterpreter = () => { + // eslint-disable-next-line + const [localRecipe, setRecipe] = useState(recipe) + + const [subscriptionResponse] = useSubscription( + { + query: ` + subscription { + operation { + state + } + } + `, + }, + (_prev, now) => now + ) + + // eslint-disable-next-line + const [_, createOperation] = useMutation(` + mutation ($recipePath: String!, $projectRoot: String!) { + createOperation(recipePath: $recipePath, projectRoot: $projectRoot) + } + `) + // eslint-disable-next-line + const [__, _sendEvent] = useMutation(` + mutation($event: String!, $input: String) { + sendEvent(event: $event, input: $input) + } + `) + + sendEvent = ({ event, input }) => { + if (input) { + _sendEvent({ event, input: JSON.stringify(input) }) + } else { + _sendEvent({ event }) + } + } + + const startRecipe = async () => { + if (!showRecipesList) { + log(`createOperation`) + if (!isRecipeStarted) { + isRecipeStarted = true + try { + await createOperation({ recipePath: localRecipe, projectRoot }) + } catch (e) { + log(`error creating operation`, e) + isRecipeStarted = false + } + } + + checkServerSession() + } + } + + subscriptionClient.connectionCallback = async () => { + checkServerSession() + startRecipe() + } + + if (isSubscriptionConnected) { + startRecipe() + } + + const state = + subscriptionResponse.data && + JSON.parse(subscriptionResponse.data.operation.state) + + if (showRecipesList) { + return ( + + + Select a recipe to run + { + showRecipesList = false + try { + await createOperation({ + recipePath: recipeItem, + projectRoot, + }) + } catch (e) { + log(`error creating operation`, e) + } + }} + /> + + ) + } + + if (!state) { + return ( + + Loading recipe + + ) + } + + const isDone = state.value === `done` + + if (state.value === `doneError`) { + console.log(`doneError state`, state) + } + + log(`state`, state) + log(`plan`, state.context.plan) + log(`stepResources`, state.context.stepResources) + + const staticMessages = {} + for (let step = 0; step < state.context.currentStep; step++) { + staticMessages[step] = [ + { + type: `mdx`, + key: `mdx-${step}`, + value: state.context.steps[step], + }, + ] + } + lodash.flattenDeep(state.context.stepResources).forEach((res, i) => { + staticMessages[res._currentStep].push({ + type: `resource`, + key: `finished-stuff-${i}`, + value: res._message, + }) + }) + + log(`staticMessages`, staticMessages) + log(`renderTime`, new Date()) + + if (isDone) { + process.nextTick(() => { + subscriptionClient.close() + log(`The recipe finished successfully`) + lodash.flattenDeep(state.context.stepResources).forEach((res, i) => { + log(`✅ ${res._message}\n`) + }) + }) + } + + const plansWithoutInputs = state.context.plan?.filter( + p => p.resourceName !== `Input` + ) + + const groupedPlans = lodash.groupBy(plansWithoutInputs, p => p.resourceName) + + return ( + <> + {` `} + p.resourceName !== `Input` && p.resourceDefinitions?._key + ) || [] + } + > + + + +
+
+ + {state.context.exports?.join(`\n`) + + `\n\n` + + state.context.steps[0]} + +
+ +
+
+ + {plansWithoutInputs?.length} + {` `} + Changes + + {Object.entries(groupedPlans).map(([resourceName, plans]) => ( +
+ + {resourceName} + + + {plans + .filter(p => p.resourceName !== `Input`) + .map((p, i) => ( + + + + + ))} + +
+ ))} +
+ + + {state.context.steps.length - 1} Steps + + {state.context.steps.slice(1).map((step, i) => ( + + ))} +
+
+
+ + ) +} + +const WithProviders = ({ children }) => { + const baseTheme = getTheme() + + const theme = { + ...baseTheme, + colors: { + ...baseTheme.colors, + background: `white`, + }, + fontWeights: { + ...baseTheme.fontWeights, + }, + styles: { + h1: { + fontSize: 6, + fontFamily: `heading`, + fontWeight: `heading`, + mt: 0, + mb: 4, + }, + h2: { + fontSize: 5, + fontFamily: `heading`, + fontWeight: `heading`, + mt: 0, + mb: 4, + }, + p: { + color: baseTheme.tones.NEUTRAL.dark, + fontSize: 2, + fontFamily: `body`, + fontWeight: `body`, + mt: 0, + mb: 4, + lineHeight: 1.45, + }, + pre: { + fontFamily: baseTheme.fonts.monospace, + fontSize: 0, + lineHeight: 1.45, + mt: 0, + mb: 6, + whiteSpace: `pre-wrap`, + }, + inlineCode: { + backgroundColor: `hsla(0,0%,0%,0.06)`, + color: baseTheme.tones.NEUTRAL.darker, + borderRadius: `3px`, + py: `0.2em`, + px: `0.2em`, + fontSize: `90%`, + }, + ol: { + color: baseTheme.tones.NEUTRAL.dark, + paddingLeft: 8, + mt: 0, + mb: 6, + fontFamily: `body`, + fontWeight: `body`, + }, + ul: { + color: baseTheme.tones.NEUTRAL.dark, + paddingLeft: 8, + mt: 0, + mb: 6, + fontFamily: `body`, + fontWeight: `body`, + }, + li: { + color: baseTheme.tones.NEUTRAL.dark, + mb: 2, + fontFamily: `body`, + fontWeight: `body`, + lineHeight: 1.6, + }, + }, + } + + return ( + + + +
{children}
+
+
+
+ ) +} + +const GlobalStyles = () => ( + +) + +export default () => ( + + + + +) diff --git a/packages/gatsby-recipes/src/index.js b/packages/gatsby-recipes/src/index.js index c48bc30eb1a28..c152ecc7bb585 100644 --- a/packages/gatsby-recipes/src/index.js +++ b/packages/gatsby-recipes/src/index.js @@ -1,8 +1,7 @@ import cli from "./cli" - -export { startGraphQLServer } from "./graphql-server" +import startGraphQLServer from "./graphql-server" // data = { recipe?: string, graphqlPort: number, projectRoot: string } const recipesHandler = async data => cli(data) -export default recipesHandler +export { startGraphQLServer, recipesHandler } diff --git a/packages/gatsby-recipes/src/parser/__snapshots__/index.test.js.snap b/packages/gatsby-recipes/src/parser/__snapshots__/index.test.js.snap deleted file mode 100644 index 912293949143d..0000000000000 --- a/packages/gatsby-recipes/src/parser/__snapshots__/index.test.js.snap +++ /dev/null @@ -1,102 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`fetches a recipe from unpkg when official short form 1`] = ` -Array [ - "# Setup Theme UI - -This recipe helps you start developing with the [Theme UI](https://theme-ui.com) styling library. - - -", - "Install packages. - - - - -", - "Add the plugin \`gatsby-plugin-theme-ui\` to your \`gatsby-config.js\`. - - -", - "Write out Theme UI configuration files. - - - - -", - "**Success**! - -You're ready to get started! - -- Read the docs: -- Learn about the theme specification: - -_note:_ if you're running this recipe on the default starter (or any other starter with -base css), you'll need to remove the require to \`layout.css\` in the \`components/layout.js\` file -as otherwise they'll override some theme-ui styles. -", -] -`; - -exports[`partitions the MDX into steps 1`] = ` -Array [ - "# Automatically run Prettier on Git commits - -Make sure all of your code is run through Prettier when you commit it to git. -We achieve this by configuring prettier to run on git hooks using husky and -lint-staged. -", - "Install packages. - - - - -", - "Implement git hooks for prettier. - - - -", - "Write prettier config files. - - - -", - "Prettier, husky, and lint-staged are now installed! You can edit your \`.prettierrc\` -if you'd like to change your prettier configuration. -", -] -`; diff --git a/packages/gatsby-recipes/src/parser/index.js b/packages/gatsby-recipes/src/parser/index.js index 921465aa81255..759605f601727 100644 --- a/packages/gatsby-recipes/src/parser/index.js +++ b/packages/gatsby-recipes/src/parser/index.js @@ -1,20 +1,61 @@ const unified = require(`unified`) const remarkMdx = require(`remark-mdx`) +const remarkMdxjs = require(`remark-mdxjs`) const remarkParse = require(`remark-parse`) const remarkStringify = require(`remark-stringify`) const fetch = require(`node-fetch`) const fs = require(`fs-extra`) const isUrl = require(`is-url`) const path = require(`path`) +const visit = require(`unist-util-visit`) +const remove = require(`unist-util-remove`) -const asRoot = nodes => { +const { uuid } = require(`./util`) + +const IGNORED_COMPONENTS = [`RecipeIntroduction`, `RecipeStep`] + +const asRoot = node => { return { type: `root`, - children: nodes, + children: [node], } } -const u = unified().use(remarkParse).use(remarkStringify).use(remarkMdx) +const pluckExports = tree => { + let exports = [] + visit(tree, `export`, node => { + exports.push(node) + }) + + remove(tree, `export`) + + return exports +} + +const applyUuid = tree => { + visit(tree, `mdxBlockElement`, node => { + if (!IGNORED_COMPONENTS.includes(node.name)) { + node.attributes.push({ + type: `mdxAttribute`, + name: `_uuid`, + value: uuid(), + }) + node.attributes.push({ + type: `mdxAttribute`, + name: `_type`, + value: node.name, + }) + } + }) + + return tree +} + +const u = unified() + .use(remarkParse) + .use(remarkStringify) + .use(remarkMdx) + .use(remarkMdxjs) const partitionSteps = ast => { const steps = [] @@ -34,18 +75,53 @@ const partitionSteps = ast => { } const toMdx = nodes => { - const stepAst = asRoot(nodes) - return u.stringify(stepAst) + const stepAst = applyUuid(asRoot(nodes)) + const mdxSrc = u.stringify(stepAst) + + return mdxSrc } const parse = async src => { try { const ast = u.parse(src) - const steps = partitionSteps(ast) + const exportNodes = pluckExports(ast) + const [intro, ...resourceSteps] = partitionSteps(ast) + + const wrappedIntroStep = { + type: `mdxBlockElement`, + name: `RecipeIntroduction`, + attributes: [], + children: intro, + } + + const wrappedResourceSteps = resourceSteps.map((step, i) => { + return { + type: `mdxBlockElement`, + name: `RecipeStep`, + attributes: [ + { + type: `mdxAttribute`, + name: `step`, + value: String(i + 1), + }, + { + type: `mdxAttribute`, + name: `totalSteps`, + value: String(resourceSteps.length), + }, + ], + children: step, + } + }) + + const steps = [wrappedIntroStep, ...wrappedResourceSteps] + ast.children = [...exportNodes, ...ast.children] return { ast, steps, + exports: exportNodes, + exportsAsMdx: exportNodes.map(toMdx), stepsAsMdx: steps.map(toMdx), } } catch (e) { diff --git a/packages/gatsby-recipes/src/parser/index.test.js b/packages/gatsby-recipes/src/parser/index.test.js index 7647167b24e1b..39b9f5205a800 100644 --- a/packages/gatsby-recipes/src/parser/index.test.js +++ b/packages/gatsby-recipes/src/parser/index.test.js @@ -13,7 +13,10 @@ jest.setTimeout(100000) test(`fetches a recipe from unpkg when official short form`, async () => { const result = await parser(`theme-ui`) - expect(result.stepsAsMdx).toMatchSnapshot() + expect(result.stepsAsMdx[0]).toMatch(`# Setup Theme UI`) + expect(result.stepsAsMdx[1]).toMatch(`Install packages`) + expect(result.stepsAsMdx[1]).toMatch(` { +// XXX: This needs to be updated for MDX v2, there error will be native +// from the parser and we're currently swallowing it +test.skip(`validate returns a syntax error`, () => { const result = validate(fixture) expect(result.line).toEqual(7) diff --git a/packages/gatsby-recipes/src/providers/contentful/client.js b/packages/gatsby-recipes/src/providers/contentful/client.js new file mode 100644 index 0000000000000..a924d4695daf9 --- /dev/null +++ b/packages/gatsby-recipes/src/providers/contentful/client.js @@ -0,0 +1,11 @@ +import { createClient } from "contentful-management" + +let client + +if (process.env.CONTENTFUL_ACCESS_TOKEN) { + client = createClient({ + accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, + }) +} + +export default client diff --git a/packages/gatsby-recipes/src/providers/contentful/entry.js b/packages/gatsby-recipes/src/providers/contentful/entry.js new file mode 100644 index 0000000000000..191e80d6b1518 --- /dev/null +++ b/packages/gatsby-recipes/src/providers/contentful/entry.js @@ -0,0 +1,80 @@ +import Joi from "@hapi/joi" + +import resourceSchema from "../resource-schema" +import client from "./client" +const getDiff = require(`../utils/get-diff`) + +const create = async (context, { fields }) => { + const spaceId = context.ContentfulSpace.id + const contentTypeId = context.ContentfulType.id + const space = await client.getSpace(spaceId) + + const entry = await space.createEntryWithId(contentTypeId, `pizza-face`, { + fields: { + title: { + "en-US": fields.title, + }, + body: { + "en-US": fields.body, + }, + }, + }) + + await entry.publish() + + console.log(`new entry`, entry) + + return { + ...entry, + id: entry.sys.id, + _message: message(entry), + } +} + +const read = async (context, name) => {} + +const destroy = async (_context, id) => {} + +const all = async () => {} + +const schema = { + fields: Joi.object(), + sys: Joi.object(), + ...resourceSchema, +} + +const validate = resource => + Joi.validate(resource, schema, { abortEarly: false }) + +const plan = async (context, { id, fields }) => { + // const currentResource = await read(context, id) + let currentResource + console.log({ context }) + + if (!currentResource) { + return { + currentState: ``, + describe: `Create Contentful entry for "${context.ContentfulType.name}"`, + diff: getDiff({}, fields), + // diffExists: true, + // skipDiff: true, + } + } else { + return { + currentState: currentResource, + describe: `Contentful type ${currentResource.name} already exists`, + // diff: getDiff(plan.currentState, plan.newState), + } + } +} + +const message = resource => `Created Contentful Entry "${resource.name}"` + +module.exports.schema = schema +module.exports.validate = validate +module.exports.plan = plan +module.exports.create = create +module.exports.read = read +module.exports.update = create +module.exports.destroy = destroy +module.exports.all = all diff --git a/packages/gatsby-recipes/src/providers/contentful/environment.js b/packages/gatsby-recipes/src/providers/contentful/environment.js new file mode 100644 index 0000000000000..67b16a4b56f48 --- /dev/null +++ b/packages/gatsby-recipes/src/providers/contentful/environment.js @@ -0,0 +1,65 @@ +import Joi from "@hapi/joi" + +import client from "./client" +import resourceSchema from "../resource-schema" + +const create = async (context, { name }) => { + const spaceId = context.ContentfulSpace.id + const space = await client.getSpace(spaceId) + const environment = await space.createEnvironment({ name }) + + return { + name: environment.name, + id: environment.sys.id, + _message: message(environment), + } +} + +const read = async (context, name) => { + console.log({ context, name }) + + return undefined +} + +const destroy = async (_context, id) => {} + +const all = async () => {} + +const schema = { + name: Joi.string(), + ...resourceSchema, +} + +const validate = resource => + Joi.validate(resource, schema, { abortEarly: false }) + +const plan = async (context, { id, name }) => { + console.log({ context, name, id }) + const currentResource = await read(context, id || name) + + if (!currentResource) { + return { + currentState: ``, + describe: `Create Contentful environment ${name}`, + diffExists: true, + skipDiff: true, + } + } else { + return { + currentState: currentResource, + describe: `Contentful environment ${name} already exists`, + diff: ``, + } + } +} + +const message = resource => `Created Contentful environment "${resource.name}"` + +module.exports.schema = schema +module.exports.validate = validate +module.exports.plan = plan +module.exports.create = create +module.exports.read = read +module.exports.update = create +module.exports.destroy = destroy +module.exports.all = all diff --git a/packages/gatsby-recipes/src/providers/contentful/space.js b/packages/gatsby-recipes/src/providers/contentful/space.js new file mode 100644 index 0000000000000..d206c86f88962 --- /dev/null +++ b/packages/gatsby-recipes/src/providers/contentful/space.js @@ -0,0 +1,94 @@ +import Joi from "@hapi/joi" + +import client from "./client" +import resourceSchema from "../resource-schema" + +const create = async (_context, { name }) => { + const space = await client.createSpace({ name }) + const deliveryAccessToken = await space.createApiKey({ + name: `gatsby`, + }) + + return { + name: space.name, + id: space.sys.id, + deliveryAccessToken: deliveryAccessToken.accessToken, + _message: message(space), + } +} + +const read = async (_context, name) => { + const spaces = await all() + console.log(`all spaces`, spaces) + + if (!spaces) { + return null + } + + const space = spaces.find(s => s.name === name) + + return { + ...space, + id: space.sys.id, + } +} + +const destroy = async (_context, id) => { + const space = await client.getSpace(id) + + const spaceResource = { + name: space.name, + id: space.sys.id, + } + + await space.delete() + + return spaceResource +} + +const all = async () => { + const spaces = client.getSpaces() + + return spaces.items +} + +const schema = { + name: Joi.string(), + ...resourceSchema, +} + +const validate = resource => + Joi.validate(resource, schema, { abortEarly: false }) + +const plan = async (context, { id, name }) => { + const currentResource = await read(context, id || name) + + if (!currentResource) { + return { + name, + id: `(Known after install)`, + deliveryAccessToken: `(Known after install)`, + currentState: ``, + describe: `Create Contentful space "${name}"`, + diffExists: true, + skipDiff: true, + } + } else { + return { + currentState: currentResource, + describe: `Contentful space ${name} already exists`, + diff: ``, + } + } +} + +const message = resource => `Created Contentful space ${resource.name}` + +module.exports.schema = schema +module.exports.validate = validate +module.exports.plan = plan +module.exports.create = create +module.exports.read = read +module.exports.update = create +module.exports.destroy = destroy +module.exports.all = all diff --git a/packages/gatsby-recipes/src/providers/contentful/type.js b/packages/gatsby-recipes/src/providers/contentful/type.js new file mode 100644 index 0000000000000..ef4586eff81a2 --- /dev/null +++ b/packages/gatsby-recipes/src/providers/contentful/type.js @@ -0,0 +1,75 @@ +import Joi from "@hapi/joi" + +import client from "./client" +import resourceSchema from "../resource-schema" +import getGraphqlFields from "../utils/get-graphql-fields" +const getDiff = require(`../utils/get-diff`) + +const GRAPHQL_FIELD_OPTIONS = { + metadata: [`type`, `name`], +} + +const create = async (context, { schema }) => { + const spaceId = context.ContentfulSpace.id + const space = await client.getSpace(spaceId) + + const fields = getGraphqlFields(schema, GRAPHQL_FIELD_OPTIONS)[0] + const contentType = await space.createContentTypeWithId(fields.name, fields) + await contentType.publish() + + return { + name: contentType.name, + id: contentType.sys.id, + _message: message(contentType), + } +} + +const read = async (context, name) => {} + +const destroy = async (_context, id) => {} + +const all = async () => {} + +const schema = { + schema: Joi.string(), + ...resourceSchema, +} + +const validate = resource => + Joi.validate(resource, schema, { abortEarly: false }) + +const plan = async (context, { id, schema }) => { + const currentResource = await read(context, id) + + const fields = getGraphqlFields(schema, GRAPHQL_FIELD_OPTIONS)[0] + console.log(`contentful type`, { currentResource, schema, id, fields }) + + if (!currentResource) { + return { + id: `(Known after install)`, + name: fields.name, + currentState: ``, + describe: `Create Contentful type "${fields.name}"`, + diff: getDiff({}, fields), + // diffExists: true, + // skipDiff: true, + } + } else { + return { + currentState: currentResource, + describe: `Contentful type ${currentResource.name} already exists`, + // diff: getDiff(plan.currentState, plan.newState), + } + } +} + +const message = resource => `Created Contentful type "${resource.name}"` + +module.exports.schema = schema +module.exports.validate = validate +module.exports.plan = plan +module.exports.create = create +module.exports.read = read +module.exports.update = create +module.exports.destroy = destroy +module.exports.all = all diff --git a/packages/gatsby-recipes/src/providers/fs/file.js b/packages/gatsby-recipes/src/providers/fs/file.js index dcb661464e146..3065a90bb64c9 100644 --- a/packages/gatsby-recipes/src/providers/fs/file.js +++ b/packages/gatsby-recipes/src/providers/fs/file.js @@ -103,7 +103,7 @@ module.exports.plan = async (context, { id, path: filePath, content }) => { } if (plan.currentState !== plan.newState) { - plan.diff = await getDiff(plan.currentState, plan.newState) + plan.diff = getDiff(plan.currentState, plan.newState) } return plan diff --git a/packages/gatsby-recipes/src/providers/gatsby/plugin.js b/packages/gatsby-recipes/src/providers/gatsby/plugin.js index b6fd87bed5f1b..9e38c1a5857ea 100644 --- a/packages/gatsby-recipes/src/providers/gatsby/plugin.js +++ b/packages/gatsby-recipes/src/providers/gatsby/plugin.js @@ -147,13 +147,7 @@ const getPluginsFromConfig = src => { const getConfigPath = root => path.join(root, `gatsby-config.js`) -const readConfigFile = async root => { - let src - try { - src = await fs.readFile(getConfigPath(root), `utf8`) - } catch (e) { - if (e.code === `ENOENT`) { - src = `/** +const defaultConfig = `/** * Configure your Gatsby site with this file. * * See: https://www.gatsbyjs.org/docs/gatsby-config/ @@ -162,15 +156,48 @@ const readConfigFile = async root => { module.exports = { plugins: [], }` + +const readConfigFile = async root => { + let src + try { + src = await fs.readFile(getConfigPath(root), `utf8`) + } catch (e) { + if (e.code === `ENOENT`) { + src = defaultConfig } else { throw e } } + if (src === ``) { + src = defaultConfig + } + return src } +class MissingInfoError extends Error { + constructor(foo = `bar`, ...params) { + // Pass remaining arguments (including vendor specific ones) to parent constructor + super(...params) + + // Maintains proper stack trace for where our error was thrown (only available on V8) + if (Error.captureStackTrace) { + Error.captureStackTrace(this, MissingInfoError) + } + + this.name = `MissingInfoError` + // Custom debugging information + this.foo = foo + this.date = new Date() + } +} + const create = async ({ root }, { name, options, key }) => { + // TODO generalize this — it's for the demo. + if (options?.accessToken === `(Known after install)`) { + throw new MissingInfoError({ name, options, key }) + } const configSrc = await readConfigFile(root) const prettierConfig = await prettier.resolveConfig(root) @@ -358,13 +385,13 @@ const schema = { } const validate = resource => { - if (REQUIRES_KEYS.includes(resource.name) && !resource.key) { + if (REQUIRES_KEYS.includes(resource.name) && !resource._key) { return { error: `${resource.name} requires a key to be set`, } } - if (resource.key && resource.key === resource.name) { + if (resource._key && resource._key === resource.name) { return { error: `${resource.name} requires a key to be different than the plugin name`, } @@ -395,6 +422,7 @@ module.exports.plan = async ({ root }, { id, key, name, options }) => { ...prettierConfig, parser: `babel`, }) + const diff = await getDiff(configSrc, newContents) return { diff --git a/packages/gatsby-recipes/src/providers/gatsby/plugin.test.js b/packages/gatsby-recipes/src/providers/gatsby/plugin.test.js index dad936630ebd3..a34d116bb86ac 100644 --- a/packages/gatsby-recipes/src/providers/gatsby/plugin.test.js +++ b/packages/gatsby-recipes/src/providers/gatsby/plugin.test.js @@ -139,7 +139,7 @@ describe(`gatsby-plugin resource`, () => { test(`validates the gatsby-source-filesystem specifies a key that isn't equal to the name`, async () => { const result = plugin.validate({ name: `gatsby-source-filesystem`, - key: `gatsby-source-filesystem`, + _key: `gatsby-source-filesystem`, }) expect(result.error).toEqual( diff --git a/packages/gatsby-recipes/src/providers/gatsby/utils/build-plugin-node.js b/packages/gatsby-recipes/src/providers/gatsby/utils/build-plugin-node.js index 9ef055ca4711b..95bc19de6efc9 100644 --- a/packages/gatsby-recipes/src/providers/gatsby/utils/build-plugin-node.js +++ b/packages/gatsby-recipes/src/providers/gatsby/utils/build-plugin-node.js @@ -6,13 +6,16 @@ module.exports = ({ name, options, key }) => { return t.stringLiteral(name) } - const pluginWithOptions = template(` + const pluginWithOptions = template( + ` const foo = { resolve: '${name}', options: ${JSON.stringify(options, null, 2)}, ${key ? `__key: "` + key + `"` : ``} } - `)() + `, + { placeholderPattern: false } + )() return pluginWithOptions.declarations[0].init } diff --git a/packages/gatsby-recipes/src/providers/npm/__snapshots__/script.test.js.snap b/packages/gatsby-recipes/src/providers/npm/__snapshots__/script.test.js.snap index 0d0a5883178ca..5a10ce5a8dbd6 100644 --- a/packages/gatsby-recipes/src/providers/npm/__snapshots__/script.test.js.snap +++ b/packages/gatsby-recipes/src/providers/npm/__snapshots__/script.test.js.snap @@ -2,7 +2,7 @@ exports[`npm script resource e2e script resource test: NPMScript create 1`] = ` Object { - "_message": "Wrote script apple to your package.json", + "_message": "Added script \\"apple\\" to your package.json", "command": "foot", "id": "apple", "name": "apple", @@ -12,7 +12,7 @@ Object { exports[`npm script resource e2e script resource test: NPMScript create plan 1`] = ` Object { "currentState": "", - "describe": "Add new command to your package.json", + "describe": "Add script \\"apple\\" to your package.json", "diff": "- Original - 1 + Modified + 3 @@ -31,7 +31,7 @@ exports[`npm script resource e2e script resource test: NPMScript destroy 1`] = ` exports[`npm script resource e2e script resource test: NPMScript update 1`] = ` Object { - "_message": "Wrote script apple to your package.json", + "_message": "Added script \\"apple\\" to your package.json", "command": "foot2", "id": "apple", "name": "apple", @@ -41,7 +41,7 @@ Object { exports[`npm script resource e2e script resource test: NPMScript update plan 1`] = ` Object { "currentState": "\\"apple\\": \\"foot\\"", - "describe": "Add new command to your package.json", + "describe": "Add script \\"apple\\" to your package.json", "diff": "- Original - 1 + Modified + 1 diff --git a/packages/gatsby-recipes/src/providers/npm/script.js b/packages/gatsby-recipes/src/providers/npm/script.js index 1fa55c28e5f75..31c699a3fadc2 100644 --- a/packages/gatsby-recipes/src/providers/npm/script.js +++ b/packages/gatsby-recipes/src/providers/npm/script.js @@ -34,7 +34,7 @@ const read = async ({ root }, id) => { id, name: id, command: pkg.scripts[id], - _message: `Wrote script ${id} to your package.json`, + _message: `Added script "${id}" to your package.json`, } } @@ -89,7 +89,7 @@ module.exports.plan = async ({ root }, { name, command }) => { currentState, newState: scriptDescription(name, command), diff, - describe: `Add new command to your package.json`, + describe: `Add script "${name}" to your package.json`, } } diff --git a/packages/gatsby-recipes/src/providers/resource-schema.js b/packages/gatsby-recipes/src/providers/resource-schema.js index 3d7f6a9de542d..12d44937d42bc 100644 --- a/packages/gatsby-recipes/src/providers/resource-schema.js +++ b/packages/gatsby-recipes/src/providers/resource-schema.js @@ -3,5 +3,6 @@ const Joi = require(`@hapi/joi`) module.exports = { id: Joi.string(), key: Joi.string(), + _key: Joi.string(), _message: Joi.string(), } diff --git a/packages/gatsby-recipes/src/providers/utils/get-diff.js b/packages/gatsby-recipes/src/providers/utils/get-diff.js index 5e282cd54f957..7b027aa7904b6 100644 --- a/packages/gatsby-recipes/src/providers/utils/get-diff.js +++ b/packages/gatsby-recipes/src/providers/utils/get-diff.js @@ -2,7 +2,7 @@ const diff = require(`jest-diff`).default const chalk = require(`chalk`) const stripAnsi = require(`strip-ansi`) -module.exports = async (oldVal, newVal) => { +module.exports = (oldVal, newVal) => { const options = { aAnnotation: `Original`, bAnnotation: `Modified`, diff --git a/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.js b/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.js index 20b4040d0ac6b..55b8a37275df8 100644 --- a/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.js +++ b/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.js @@ -1,14 +1,18 @@ const { makeExecutableSchema } = require(`@graphql-tools/schema`) const { SchemaDirectiveVisitor } = require(`@graphql-tools/utils`) -const gqlFieldsToObject = fields => +const gqlFieldsToArray = fields => Object.entries(fields).reduce((acc, [key, value]) => { - acc[key] = { + const metadata = value.metadata || {} + const field = { + id: key, type: value.type, - metadata: value.metadata, + name: key, + ...metadata, } - return acc - }, {}) + + return [...acc, field] + }, []) class MetadataDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { @@ -53,7 +57,7 @@ module.exports = (typeDefs, { metadata } = {}) => { .map(([key, value]) => { return { name: key, - fields: gqlFieldsToObject(value._fields), + fields: gqlFieldsToArray(value._fields), } }) } diff --git a/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.test.js b/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.test.js index 5670bc99024ef..02743fbea5d1d 100644 --- a/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.test.js +++ b/packages/gatsby-recipes/src/providers/utils/get-graphql-fields.test.js @@ -17,19 +17,19 @@ test(`get-graphql-fields returns an array of fields`, () => { expect(result).toMatchInlineSnapshot(` Array [ Object { - "fields": Object { - "body": Object { - "metadata": Object { - "title": "Content", - "type": "Text", - }, + "fields": Array [ + Object { + "id": "title", + "name": "title", "type": "String", }, - "title": Object { - "metadata": undefined, - "type": "String", + Object { + "id": "body", + "name": "body", + "title": "Content", + "type": "Text", }, - }, + ], "name": "BlogPost", }, ] diff --git a/packages/gatsby-recipes/src/recipe-machine/__snapshots__/index.test.js.snap b/packages/gatsby-recipes/src/recipe-machine/__snapshots__/index.test.js.snap deleted file mode 100644 index 00e629826bab8..0000000000000 --- a/packages/gatsby-recipes/src/recipe-machine/__snapshots__/index.test.js.snap +++ /dev/null @@ -1,18 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`recipe-machine stores created/changed/deleted resources on the context after applying plan 1`] = ` -Array [ - Object { - "_currentStep": 1, - "_message": "Wrote file ./hi.md", - }, - Object { - "_currentStep": 1, - "_message": "Wrote file ./hi2.md", - }, - Object { - "_currentStep": 2, - "_message": "Wrote file ./hi3.md", - }, -] -`; diff --git a/packages/gatsby-recipes/src/recipe-machine/index.js b/packages/gatsby-recipes/src/recipe-machine/index.js index df30d42067c16..9fbdc107c2b10 100644 --- a/packages/gatsby-recipes/src/recipe-machine/index.js +++ b/packages/gatsby-recipes/src/recipe-machine/index.js @@ -1,4 +1,6 @@ -const { Machine, assign } = require(`xstate`) +const { Machine, assign, send } = require(`xstate`) + +const debug = require(`debug`)(`recipes-machine`) const createPlan = require(`../create-plan`) const applyPlan = require(`../apply-plan`) @@ -14,10 +16,13 @@ const recipeMachine = Machine( projectRoot: null, currentStep: 0, steps: [], + exports: [], plan: [], commands: [], stepResources: [], stepsAsMdx: [], + exportsAsMdx: [], + inputs: {}, }, states: { parsingRecipe: { @@ -26,6 +31,8 @@ const recipeMachine = Machine( src: async (context, _event) => { let parsed + debug(`parsingRecipe`) + if (context.src) { parsed = await parser.parse(context.src) } else if (context.recipePath && context.projectRoot) { @@ -38,17 +45,22 @@ const recipeMachine = Machine( ) } + debug(`parsedRecipe`) + return parsed }, onError: { target: `doneError`, actions: assign({ error: (context, event) => { + debug(`error parsing recipes`) + let msg try { msg = JSON.parse(event.data.message) return msg } catch (e) { + console.log(e) return { error: `Could not parse recipe ${context.recipePath}`, e, @@ -61,6 +73,7 @@ const recipeMachine = Machine( target: `validateSteps`, actions: assign({ steps: (context, event) => event.data.stepsAsMdx, + exports: (context, event) => event.data.exportsAsMdx, }), }, }, @@ -69,8 +82,10 @@ const recipeMachine = Machine( invoke: { id: `validateSteps`, src: async (context, event) => { + debug(`validatingSteps`) const result = await validateSteps(context.steps) if (result.length > 0) { + debug(`errors found in validation`) throw new Error(JSON.stringify(result)) } @@ -89,11 +104,12 @@ const recipeMachine = Machine( entry: [`deleteOldPlan`], invoke: { id: `createPlan`, - src: async (context, event) => { + src: (context, event) => async (cb, onReceive) => { try { - const result = await createPlan(context) + const result = await createPlan(context, cb) return result } catch (e) { + console.log(e) throw e } }, @@ -106,14 +122,41 @@ const recipeMachine = Machine( onError: { target: `doneError`, actions: assign({ - error: (context, event) => event.data.errors || event.data, + error: (context, event) => event.data?.errors || event.data, + }), + }, + }, + on: { + INVALID_PROPS: { + target: `doneError`, + actions: assign({ + error: (context, event) => event.data, }), }, }, }, presentPlan: { + invoke: { + id: `presentingPlan`, + src: (context, event) => (cb, onReceive) => { + onReceive(async e => { + context.inputs = context.inputs || {} + context.inputs[e.data.key] = e.data + const result = await createPlan(context, cb) + cb({ type: `onUpdatePlan`, data: result }) + }) + }, + }, on: { CONTINUE: `applyingPlan`, + INPUT_ADDED: { + actions: send((context, event) => event, { to: `presentingPlan` }), + }, + onUpdatePlan: { + actions: assign({ + plan: (context, event) => event.data, + }), + }, }, }, applyingPlan: { @@ -122,6 +165,7 @@ const recipeMachine = Machine( invoke: { id: `applyPlan`, src: (context, event) => cb => { + debug(`applying plan`) cb(`RESET`) if (context.plan.length === 0) { return cb(`onDone`) @@ -131,11 +175,16 @@ const recipeMachine = Machine( cb(`TICK`) }, 10000) - applyPlan(context.plan) + applyPlan(context, cb) .then(result => { + debug(`applied plan`) cb({ type: `onDone`, data: result }) }) - .catch(error => cb({ type: `onError`, data: error })) + .catch(error => { + debug(`error applying plan`) + debug(error) + cb({ type: `onError`, data: error }) + }) return () => clearInterval(interval) }, @@ -200,25 +249,32 @@ const recipeMachine = Machine( }), addResourcesToContext: assign((context, event) => { if (event.data) { - const stepResources = context.stepResources || [] - const messages = event.data.map(e => { - return { - _message: e._message, - _currentStep: context.currentStep, - } + let plan = context.plan || [] + plan = plan.map(p => { + const changedResource = event.data.find(c => { + if (c._uuid) { + return c._uuid === p._uuid + } else { + return c.resourceDefinitions._key === p.resourceDefinitions._key + } + }) + if (!changedResource) return p + p._message = changedResource._message + p.isDone = true + return p }) return { - stepResources: stepResources.concat(messages), + plan, } } return undefined }), }, guards: { - hasNextStep: (context, event) => - context.currentStep < context.steps.length, - atLastStep: (context, event) => - context.currentStep === context.steps.length, + hasNextStep: (context, event) => false, + // false || context.currentStep < context.steps.length, + atLastStep: (context, event) => true, + // true || context.currentStep === context.steps.length, }, } ) diff --git a/packages/gatsby-recipes/src/recipe-machine/index.test.js b/packages/gatsby-recipes/src/recipe-machine/index.test.js index 044030c4c0dd5..8feba8ceb1a9d 100644 --- a/packages/gatsby-recipes/src/recipe-machine/index.test.js +++ b/packages/gatsby-recipes/src/recipe-machine/index.test.js @@ -40,13 +40,9 @@ describe(`recipe-machine`, () => { recipeMachine.withContext(initialContext) ).onTransition(state => { if (state.value === `presentPlan`) { - if (state.context.currentStep === 0) { - service.send(`CONTINUE`) - } else { - expect(state.context.plan).toBeTruthy() - service.stop() - done() - } + expect(state.context.plan).toBeTruthy() + service.stop() + done() } }) @@ -84,47 +80,4 @@ describe(`recipe-machine`, () => { service.start() }) - - it(`stores created/changed/deleted resources on the context after applying plan`, done => { - const filePath = `./hi.md` - const filePath2 = `./hi2.md` - const filePath3 = `./hi3.md` - const initialContext = { - src: ` -# File! - ---- - - - - ---- - - - `, - currentStep: 0, - } - const service = interpret( - recipeMachine.withContext(initialContext) - ).onTransition(state => { - // Keep simulating moving onto the next step - if (state.value === `presentPlan`) { - service.send(`CONTINUE`) - } - if (state.value === `done`) { - // Clean up files - fs.unlinkSync(path.join(process.cwd(), filePath)) - fs.unlinkSync(path.join(process.cwd(), filePath2)) - fs.unlinkSync(path.join(process.cwd(), filePath3)) - - expect(state.context.stepResources).toHaveLength(3) - expect(state.context.stepResources).toMatchSnapshot() - expect(state.context.stepResources[0]._message).toBeTruthy() - expect(state.context.stepResources[0]._currentStep).toBeTruthy() - done() - } - }) - - service.start() - }) }) diff --git a/packages/gatsby-recipes/src/renderer/babel-plugin-copy-key-prop.js b/packages/gatsby-recipes/src/renderer/babel-plugin-copy-key-prop.js new file mode 100644 index 0000000000000..8473fcec22fa8 --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/babel-plugin-copy-key-prop.js @@ -0,0 +1,17 @@ +module.exports = api => { + const { types: t } = api + + return { + visitor: { + JSXIdentifier(path) { + if (path.node.name === `key`) { + const clonePath = t.cloneNode(path.node) + const cloneParent = t.cloneNode(path.parent) + clonePath.name = `_key` + cloneParent.name = clonePath + path.parentPath.container.push(cloneParent) + } + }, + }, + } +} diff --git a/packages/gatsby-recipes/src/renderer/babel-plugin-move-export-keywords.js b/packages/gatsby-recipes/src/renderer/babel-plugin-move-export-keywords.js new file mode 100644 index 0000000000000..26e89760ffd56 --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/babel-plugin-move-export-keywords.js @@ -0,0 +1,56 @@ +const _ = require(`lodash`) + +const isMDXLayout = element => element.name && element.name.name === `MDXLayout` +const isLayoutProps = declaration => declaration.id.name === `layoutProps` +const shouldRemoveDeclaration = declaration => + isLayoutProps(declaration) || isMDXLayout(declaration) + +module.exports = api => { + const { types: t } = api + let exportsToMove = [] + return { + visitor: { + ExportNamedDeclaration(path) { + const declaration = path.node.declaration + + // Ignore "export { Foo as default }" syntax + if (declaration) { + path.replaceWith(declaration) + exportsToMove.push(t.cloneNode(path.node)) + path.remove() + } + }, + VariableDeclaration(path) { + const declaration = path.node.declarations[0] + if (!declaration) { + return + } + + if (shouldRemoveDeclaration(declaration)) { + path.remove() + } + }, + FunctionDeclaration(path) { + if (path.node.id.name == `MDXContent`) { + exportsToMove = _.uniqBy( + exportsToMove, + e => e.declarations[0].id.name + ) + exportsToMove.forEach(node => + path.get(`body`).unshiftContainer(`body`, node) + ) + } + }, + // MDXLayout => div + JSXElement(path) { + const { openingElement, closingElement } = path.node + + if (isMDXLayout(openingElement)) { + openingElement.name = t.JSXIdentifier(`div`) + openingElement.attributes = [] + closingElement.name = t.JSXIdentifier(`div`) + } + }, + }, + } +} diff --git a/packages/gatsby-recipes/src/renderer/babel-plugin-remove-shortcodes.js b/packages/gatsby-recipes/src/renderer/babel-plugin-remove-shortcodes.js new file mode 100644 index 0000000000000..0b65b93419882 --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/babel-plugin-remove-shortcodes.js @@ -0,0 +1,74 @@ +const isShortcode = declaration => + declaration.init?.callee?.name === `makeShortcode` +const isShortcodeFunction = declaration => + declaration.id.name === `makeShortcode` +const isMDXLayout = element => element.name && element.name.name === `MDXLayout` +const isMDXContent = declaration => declaration.id.name === `MDXContent` +const isLayoutProps = declaration => declaration.id.name === `layoutProps` +const isMDXContentStaticProperty = node => { + if (node.object.name !== `MDXContent`) { + return false + } + + return node.property.name === `isMDXComponent` +} + +const shouldRemoveDeclaration = declaration => + isShortcode(declaration) || + isShortcodeFunction(declaration) || + isMDXLayout(declaration) || + isLayoutProps(declaration) + +module.exports = api => { + const { types: t } = api + + return { + visitor: { + // Remove unneeded variables that MDX outputs but we won't use + VariableDeclaration(path) { + const declaration = path.node.declarations[0] + if (!declaration) { + return + } + + if (shouldRemoveDeclaration(declaration)) { + path.remove() + } + }, + + // Unwrap the MDXContent component instantiation because we will + // evaluate the function directly. This ensures that React properly + // handles rendering by not re-mounting the component every render. + FunctionDeclaration(path) { + if (!isMDXContent(path.node)) { + return + } + + // path.traverse({ + // ReturnStatement(innerPath) { + // path.replaceWith(innerPath.node) + // }, + // }) + }, + + // MDXLayout => doc + JSXElement(path) { + const { openingElement, closingElement } = path.node + + if (isMDXLayout(openingElement)) { + openingElement.name = t.JSXIdentifier(`doc`) + openingElement.attributes = [] + closingElement.name = t.JSXIdentifier(`doc`) + } + }, + + // Remove the MDXContent.isMDXContent = true declaration since + // we've removed MDXContent entirely + MemberExpression(path) { + if (isMDXContentStaticProperty(path.node)) { + path.parentPath.remove() + } + }, + }, + } +} diff --git a/packages/gatsby-recipes/src/renderer/index.js b/packages/gatsby-recipes/src/renderer/index.js index 575eaa4fb279a..32487ad8a2573 100644 --- a/packages/gatsby-recipes/src/renderer/index.js +++ b/packages/gatsby-recipes/src/renderer/index.js @@ -1,55 +1,50 @@ const React = require(`react`) -const mdx = require(`@mdx-js/mdx`) -const { transform } = require(`@babel/standalone`) -const babelPluginTransformReactJsx = require(`@babel/plugin-transform-react-jsx`) const { render } = require(`./render`) -const resourceComponents = require(`./resource-components`) +const { resourceComponents } = require(`./resource-components`) +const { RecipeStep, RecipeIntroduction } = require(`./step-component`) +const Input = require(`./input`).default +const { useInput, useInputByKey } = require(`./input-provider`) +const { useResource } = require(`./resource-provider`) +const { useProvider } = require(`./provider-provider`) + +const transformRecipeMDX = require(`../transform-recipe-mdx`).default const scope = { React, + RecipeStep, + RecipeIntroduction, + Input, + useInput, + useInputByKey, + useResource, + useProvider, Config: `div`, // Keep this as a noop for now ...resourceComponents, + mdx: React.createElement, + MDXContent: React.createElement, } -// We want to call the function constructor with our resulting -// transformed JS so we need to turn it into a "function body" const transformCodeForEval = code => { - const newCode = code.replace(/;$/, ``) - - return `return (${newCode})` -} - -// TODO: Release MDX v2 canary so we can avoid the hacks -const stripMdxLayout = str => { - const newJsx = str - .replace(/^.*mdxType="MDXLayout">/ms, ``) - .replace(/<\/MDXLayout>.*/ms, ``) - - return `${newJsx}` -} + // Remove the trailing semicolons so we can turn the component + // into a return statement. + let newCode = code.replace(/;\n;$/, ``) -const transformJsx = jsx => { - const { code } = transform(jsx, { - plugins: [[babelPluginTransformReactJsx, { useBuiltIns: true }]], - }) + newCode = newCode + `\nreturn React.createElement(MDXContent)` - return code + return newCode } -// This is overloaded to handle MDX input, JSX input -// or MDX's JSX output as input. -module.exports = jsx => { +module.exports = (mdxSrc, cb, context, isApply) => { const scopeKeys = Object.keys(scope) const scopeValues = Object.values(scope) - const jsxFromMdx = mdx.sync(jsx, { skipExport: true }) - const srcCode = transformJsx(stripMdxLayout(jsxFromMdx)) + const srcCode = transformRecipeMDX(mdxSrc, true) const component = new Function(...scopeKeys, transformCodeForEval(srcCode)) try { - const result = render(component(...scopeValues)) + const result = render(component(...scopeValues), cb, context, isApply) return result } catch (e) { throw e diff --git a/packages/gatsby-recipes/src/renderer/index.test.js b/packages/gatsby-recipes/src/renderer/index.test.js index f60524114c384..128126f2bc8fe 100644 --- a/packages/gatsby-recipes/src/renderer/index.test.js +++ b/packages/gatsby-recipes/src/renderer/index.test.js @@ -1,33 +1,48 @@ const render = require(`.`) +const { parse } = require(`../parser`) const mdxFixture = ` # Hello, world! - +--- + +export const fooContent = 'hiiiiii!' + + ` describe(`renderer`, () => { test(`handles MDX as input`, async () => { - const result = await render(mdxFixture) + const { stepsAsMdx, exportsAsMdx } = await parse(mdxFixture) + const result = await render([...stepsAsMdx, ...exportsAsMdx].join(`\n`)) // Gatsby latest version is ever changing so we use regex matcher for currentState property. // Unfortunately jest property matchers work weirdly on arrays so instead // of snapshotting entitre result array it's split into few pieces. expect(result.length).toEqual(3) + delete result[0]._uuid + delete result[1]._uuid + delete result[2]._uuid expect(result[0]).toMatchInlineSnapshot(` Object { + "_stepMetadata": Object { + "step": "1", + "totalSteps": "1", + }, + "_type": "File", "currentState": "", "describe": "Write foo.js", "diff": "- Original - 0 + Modified + 1 - + /** foo */", - "newState": "/** foo */", + + hiiiiii!", + "newState": "hiiiiii!", "resourceDefinitions": Object { - "content": "/** foo */", + "content": "hiiiiii!", + "mdxType": "File", "path": "foo.js", }, "resourceName": "File", @@ -35,6 +50,11 @@ describe(`renderer`, () => { `) expect(result[1]).toMatchInlineSnapshot(` Object { + "_stepMetadata": Object { + "step": "1", + "totalSteps": "1", + }, + "_type": "File", "currentState": "", "describe": "Write foo2.js", "diff": "- Original - 0 @@ -44,6 +64,7 @@ describe(`renderer`, () => { "newState": "/** foo2 */", "resourceDefinitions": Object { "content": "/** foo2 */", + "mdxType": "File", "path": "foo2.js", }, "resourceName": "File", @@ -55,10 +76,16 @@ describe(`renderer`, () => { }, ` Object { + "_stepMetadata": Object { + "step": "1", + "totalSteps": "1", + }, + "_type": "NPMPackage", "currentState": StringMatching /gatsby@\\[0-9\\.\\]\\+/, "describe": "Install gatsby@latest", "newState": "gatsby@latest", "resourceDefinitions": Object { + "mdxType": "NPMPackage", "name": "gatsby", }, "resourceName": "NPMPackage", @@ -73,6 +100,7 @@ describe(`renderer`, () => { expect(result).toMatchInlineSnapshot(` Array [ Object { + "_stepMetadata": Object {}, "currentState": "", "describe": "Write hi.md", "diff": "- Original - 0 @@ -82,6 +110,7 @@ describe(`renderer`, () => { "newState": "hi", "resourceDefinitions": Object { "content": "hi", + "mdxType": "File", "path": "hi.md", }, "resourceName": "File", @@ -90,6 +119,46 @@ describe(`renderer`, () => { `) }) + test(`handles exports and allows them to be used as local variables`, async () => { + const { stepsAsMdx, exportsAsMdx } = await parse(` +# Hello, world! + +--- + +export const myVar = 'hi.mdx' + + + `) + + const result = await render([...stepsAsMdx, ...exportsAsMdx].join(`\n`)) + result.forEach(r => delete r._uuid) + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "_stepMetadata": Object { + "step": "1", + "totalSteps": "1", + }, + "_type": "File", + "currentState": "", + "describe": "Write hi.mdx", + "diff": "- Original - 0 + + Modified + 1 + + + hello", + "newState": "hello", + "resourceDefinitions": Object { + "content": "hello", + "mdxType": "File", + "path": "hi.mdx", + }, + "resourceName": "File", + }, + ] + `) + }) + test(`returns a plan for nested JSX`, async () => { const result = await render(`
@@ -99,6 +168,7 @@ describe(`renderer`, () => { expect(result).toMatchInlineSnapshot(` Array [ Object { + "_stepMetadata": Object {}, "currentState": "", "describe": "Write foo.js", "diff": "- Original - 0 @@ -108,6 +178,7 @@ describe(`renderer`, () => { "newState": "/** foo */", "resourceDefinitions": Object { "content": "/** foo */", + "mdxType": "File", "path": "foo.js", }, "resourceName": "File", diff --git a/packages/gatsby-recipes/src/renderer/input-provider.js b/packages/gatsby-recipes/src/renderer/input-provider.js new file mode 100644 index 0000000000000..c5a29e50575da --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/input-provider.js @@ -0,0 +1,30 @@ +import React, { useContext, useState } from "react" + +const InputContext = React.createContext({}) + +export const useInputByKey = key => { + const context = useContext(InputContext) || {} + const result = context[key] + return result?.value +} + +export const useInput = ({ type = `text`, label, key = `123` }) => { + const contextVal = useInputByKey(key) || `` + const [val, setVal] = useState(contextVal) + + const Input = props => ( +
+ + setVal(e.target.value)} + /> +
+ ) + + return [Input, val] +} + +export const InputProvider = InputContext.Provider diff --git a/packages/gatsby-recipes/src/renderer/input.js b/packages/gatsby-recipes/src/renderer/input.js new file mode 100644 index 0000000000000..4d40be5911cd6 --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/input.js @@ -0,0 +1,15 @@ +// eslint-disable-next-line no-unused-vars +import React from "react" +import { useRecipeStep } from "./step-component" + +function Input(props) { + const step = useRecipeStep() + + return JSON.stringify({ + ...props, + describe: props.label, + _stepMetadata: { step: step.step }, + }) +} + +export default Input diff --git a/packages/gatsby-recipes/src/renderer/parent-resource-provider.js b/packages/gatsby-recipes/src/renderer/parent-resource-provider.js new file mode 100644 index 0000000000000..9767d3f916a1a --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/parent-resource-provider.js @@ -0,0 +1,19 @@ +import React, { useContext } from "react" + +const ParentResourceContext = React.createContext({}) + +export const useParentResourceContext = () => { + const context = useContext(ParentResourceContext) + return context +} + +export const ParentResourceProvider = ({ data: providedData, children }) => { + const parentData = useParentResourceContext() + const data = { ...parentData, ...providedData } + + return ( + + {children} + + ) +} diff --git a/packages/gatsby-recipes/src/renderer/provider-provider.js b/packages/gatsby-recipes/src/renderer/provider-provider.js new file mode 100644 index 0000000000000..58c82f2efbf2f --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/provider-provider.js @@ -0,0 +1,18 @@ +// this file has the best name +// eslint-disable-next-line +import React from "react" + +export const useProvider = provider => { + // const context = useContext(ResourceContext) + // const result = context.find(c => c.resourceDefinitions._key === key) + // return result + const providers = { + contentful: { + accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, + }, + } + + return providers[provider] +} + +// export const ResourceProvider = ResourceContext.Provider diff --git a/packages/gatsby-recipes/src/renderer/reconciler.js b/packages/gatsby-recipes/src/renderer/reconciler.js index 489f0a655c556..6c5190b5781c9 100644 --- a/packages/gatsby-recipes/src/renderer/reconciler.js +++ b/packages/gatsby-recipes/src/renderer/reconciler.js @@ -1,6 +1,12 @@ const ReactReconciler = require(`react-reconciler`) -const debug = require(`debug`)(`recipes-reconciler`) +// const debugInner = require(`debug`)(`recipes-reconciler`) +const debug = (title, data) => { + if (process.env.DEBUG) { + console.log(title, data) + } + // debugInner(title, JSON.stringify(data, null, 2)) +} const reconciler = ReactReconciler({ finalizeInitialChildren(element, type, props) {}, @@ -37,10 +43,19 @@ const reconciler = ReactReconciler({ debug(`creating text instance`, text) return { text } }, + commitTextUpdate(textInstance, oldText, newText) { + debug(`updating text instance`, newText) + textInstance.text = newText + return textInstance + }, appendChildToContainer(container, child) { - debug(`appending child to container`, { container, child }) container.children = container.children || [] - const index = container.children.findIndex(c => c.key === child.key) + const propName = child.key ? `key` : `_uuid` + const index = container.children.findIndex( + c => c[propName] === child[propName] + ) + + debug(`appending child to container at index ${index}`) if (index === -1) { container.children.push(child) @@ -94,8 +109,6 @@ const RecipesRenderer = { let container = reconciler.createContainer(currState, false, false) reconciler.updateContainer(whatToRender, container, null, null) - debug(`render result`, JSON.stringify(currState, null, 2)) - return currState }, } diff --git a/packages/gatsby-recipes/src/renderer/render.js b/packages/gatsby-recipes/src/renderer/render.js index 3c37cd0595d0b..61a320626165d 100644 --- a/packages/gatsby-recipes/src/renderer/render.js +++ b/packages/gatsby-recipes/src/renderer/render.js @@ -1,15 +1,38 @@ -const React = require(`react`) -const { Suspense } = require(`react`) +import React, { Suspense, useContext, useState } from "react" +import Queue from "p-queue" +import lodash from "lodash" -const resources = require(`../resources`) +import resources from "../resources" -const RecipesReconciler = require(`./reconciler`) -const ErrorBoundary = require(`./error-boundary`) -const transformToPlan = require(`./transform-to-plan-structure`) +import RecipesReconciler from "./reconciler" +import ErrorBoundary from "./error-boundary" +import transformToPlan from "./transform-to-plan-structure" +import { + ParentResourceProvider, + useParentResourceContext, +} from "./parent-resource-provider" +import { useRecipeStep } from "./step-component" +import { InputProvider } from "./input-provider" +import { ResourceProvider, useResourceContext } from "./resource-provider" -const promises = [] -const errors = [] -const cache = new Map() +const queue = new Queue({ concurrency: 5, autoStart: false }) + +let errors = [] +const resultCache = new Map() +const inFlightCache = new Map() + +const ModeContext = React.createContext({}) +const useMode = () => useContext(ModeContext) +const ModeProvider = ModeContext.Provider + +const getInvalidProps = errors => { + const invalidProps = errors.filter(e => { + const details = e.details + const unknownProp = details.find(e => e.type === `object.allowUnknown`) + return unknownProp + }) + return invalidProps +} const getUserProps = props => { // eslint-disable-next-line @@ -17,80 +40,224 @@ const getUserProps = props => { return userProps } -const Wrapper = ({ children }) => ( - - Loading recipe...

}>{children}
-
-) +const SetResourcesProvider = React.createContext() -const ResourceComponent = ({ _resourceName: Resource, ...props }) => { - const userProps = getUserProps(props) +let resourcesCache + +const Wrapper = ({ children, inputs, isApply }) => { + const [resourcesList, setResources] = useState(resourcesCache || []) + resourcesCache = resourcesList return ( - Reading resource...

}> + + + + + + Loading recipe...

}> + {children} +
+
+
+
+
+
+ ) +} + +const ResourceComponent = ({ + _resourceName: Resource, + _uuid, + _type, + children, + ...props +}) => { + const { mode } = useMode() + const step = useRecipeStep() + const parentResourceContext = useParentResourceContext() + const allResources = useResourceContext() + + const setResources = useContext(SetResourcesProvider) + // TODO add provider onto context + const resourceData = handleResource( + Resource, + { + ...parentResourceContext, + root: process.cwd(), + _uuid, + mode, + }, + props, + allResources, + setResources + ) + + return ( + {JSON.stringify({ - ...readResource(Resource, { root: process.cwd() }, props), - _props: userProps, + ...resourceData, + _props: props, + _stepMetadata: step, + _uuid, + _type, })} + {children} -
+ ) } -const validateResource = (resourceName, _context, props) => { +const validateResource = (resourceName, context, props) => { const userProps = getUserProps(props) const { error } = resources[resourceName].validate(userProps) + if (error) { + error.resourceUuid = context._uuid + } return error } -const readResource = (resourceName, context, props) => { +const handleResource = ( + resourceName, + context, + props, + allResources, + setResources +) => { const error = validateResource(resourceName, context, props) if (error) { errors.push(error) return null } - const key = JSON.stringify({ resourceName, ...props }) - const cachedResult = cache.get(key) + const { mode } = context + + let key + // Only run apply once per resource + if (mode === `apply`) { + key = mode + ` ` + resourceName + ` ` + props._key + } else { + key = JSON.stringify({ resourceName, ...props, mode }) + } + + const updateResource = result => { + allResources = allResources.filter(a => a.resourceDefinitions._key) + const resourceMap = new Map() + + allResources.forEach(r => resourceMap.set(r.resourceDefinitions._key, r)) + const newResource = { + resourceName, + resourceDefinitions: props, + ...result, + } + + if ( + props._key && + !lodash.isEqual(newResource, resourceMap.get(props._key)) + ) { + resourceMap.set(props._key, newResource) + setResources([...resourceMap.values()]) + } + } + + const cachedResult = resultCache.get(key) + const inFlightPromise = inFlightCache.get(key) if (cachedResult) { + updateResource(cachedResult) return cachedResult } + if (inFlightPromise) { + throw inFlightPromise + } + + const fn = mode === `apply` ? `create` : `plan` + let promise try { - promise = resources[resourceName] - .plan(context, props) - .then(result => cache.set(key, result)) - .catch(e => console.log(e)) + promise = new Promise((resolve, reject) => { + // Multiple of the same promises can be queued due to re-rendering + // so this first checks for the cached result again before executing + // the request. + const cachedValue = resultCache.get(key) + if (cachedValue) { + resolve(cachedValue) + updateResource(cachedValue) + } else { + resources[resourceName][fn](context, props) + .then(result => { + updateResource(result) + inFlightCache.set(key, false) + return result + }) + .then(result => resultCache.set(key, result)) + .then(resolve) + .catch(e => { + console.log(e) + if (e.name === `MissingInfoError`) { + inFlightCache.delete(key) + } + reject(e) + }) + } + }) } catch (e) { throw e } - promises.push(promise) + inFlightCache.set(key, promise) + + queue.add(() => promise) throw promise } -const render = async recipe => { +const render = async (recipe, cb, inputs = {}, isApply) => { const plan = {} - const recipeWithWrapper = {recipe} + const recipeWithWrapper = ( + + {recipe} + + ) + + const renderResources = async () => { + queue.pause() - try { - // Run the first pass of the render to queue up all the promises and suspend RecipesReconciler.render(recipeWithWrapper, plan) if (errors.length) { - const error = new Error(`Unable to validate resources`) - error.errors = errors - throw error + const invalidProps = getInvalidProps(errors) + + if (invalidProps.length) { + return cb({ type: `INVALID_PROPS`, data: invalidProps }) + } else { + errors = [] + } + } + + // If there aren't any new resources that need to be fetched, or errors, we're done! + if (!queue.size && !errors.length) { + return undefined + } + + queue.start() + await queue.onIdle() + return await renderResources() + } + + try { + // Begin the "render loop" until there are no more resources being queued. + await renderResources() + + if (errors.length) { + // We found errors that were emitted back to the state machine, so + // we don't need to re-render + return null } - // Await all promises for resources and cache results - await Promise.all(promises) - // Rerender with the resources and resolve the data + // Rerender with the resources and resolve the data from the cache const result = RecipesReconciler.render(recipeWithWrapper, plan) return transformToPlan(result) } catch (e) { diff --git a/packages/gatsby-recipes/src/renderer/render.test.js b/packages/gatsby-recipes/src/renderer/render.test.js index 72a7e5fa60f8e..e7ce7c361e826 100644 --- a/packages/gatsby-recipes/src/renderer/render.test.js +++ b/packages/gatsby-recipes/src/renderer/render.test.js @@ -1,7 +1,10 @@ -const React = require(`react`) +import React from "react" -const { render } = require(`./render`) -const { File, NPMPackage } = require(`./resource-components`) +import { render } from "./render" +import { resourceComponents } from "./resource-components" +import { RecipeStep } from "./step-component" + +const { File, NPMPackage } = resourceComponents const fixture = ( @@ -10,6 +13,127 @@ const fixture = ( ) +test(`handles nested rendering`, async () => { + const result = await render( + + + + + + + , + {} + ) + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "_stepMetadata": Object {}, + "currentState": "", + "describe": "Write red.js", + "diff": "- Original - 0 + + Modified + 1 + + + red!", + "newState": "red!", + "resourceChildren": Array [ + Object { + "_stepMetadata": Object {}, + "currentState": "", + "describe": "Write blue.js", + "diff": "- Original - 0 + + Modified + 1 + + + blue!", + "newState": "blue!", + "resourceChildren": Array [ + Object { + "_stepMetadata": Object {}, + "currentState": "", + "describe": "Write yellow.js", + "diff": "- Original - 0 + + Modified + 1 + + + yellow!", + "newState": "yellow!", + "resourceDefinitions": Object { + "content": "yellow!", + "path": "yellow.js", + }, + "resourceName": "File", + }, + ], + "resourceDefinitions": Object { + "content": "blue!", + "path": "blue.js", + }, + "resourceName": "File", + }, + ], + "resourceDefinitions": Object { + "content": "red!", + "path": "red.js", + }, + "resourceName": "File", + }, + ] + `) +}) + +test(`includes step metadata`, async () => { + const result = await render( + + + + + + + + + ) + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "_stepMetadata": Object { + "step": 1, + "totalSteps": 2, + }, + "currentState": "", + "describe": "Write red.js", + "diff": "- Original - 0 + + Modified + 1 + + + red!", + "newState": "red!", + "resourceDefinitions": Object { + "content": "red!", + "path": "red.js", + }, + "resourceName": "File", + }, + Object { + "_stepMetadata": Object { + "step": 2, + "totalSteps": 2, + }, + "currentState": "", + "describe": "Write blue.js", + "diff": "- Original - 0 + + Modified + 1 + + + blue!", + "newState": "blue!", + "resourceDefinitions": Object { + "content": "blue!", + "path": "blue.js", + }, + "resourceName": "File", + }, + ] + `) +}) + test(`renders to a plan`, async () => { const result = await render(fixture, {}) @@ -20,6 +144,7 @@ test(`renders to a plan`, async () => { expect(result.length).toEqual(2) expect(result[0]).toMatchInlineSnapshot(` Object { + "_stepMetadata": Object {}, "currentState": "", "describe": "Write red.js", "diff": "- Original - 0 @@ -34,12 +159,14 @@ test(`renders to a plan`, async () => { "resourceName": "File", } `) + expect(result[1]).toMatchInlineSnapshot( { currentState: expect.stringMatching(/gatsby@[0-9.]+/), }, ` Object { + "_stepMetadata": Object {}, "currentState": StringMatching /gatsby@\\[0-9\\.\\]\\+/, "describe": "Install gatsby@latest", "newState": "gatsby@latest", diff --git a/packages/gatsby-recipes/src/renderer/resource-components.js b/packages/gatsby-recipes/src/renderer/resource-components.js index b82d10bda3e8b..0d3ef21e79d0d 100644 --- a/packages/gatsby-recipes/src/renderer/resource-components.js +++ b/packages/gatsby-recipes/src/renderer/resource-components.js @@ -1,13 +1,15 @@ -const React = require(`react`) +import React, { Suspense } from "react" -const resources = require(`../resources`) +import resources from "../resources" -const { ResourceComponent } = require(`./render`) +import { ResourceComponent } from "./render" -const resourceComponents = Object.keys(resources).reduce( +export const resourceComponents = Object.keys(resources).reduce( (acc, resourceName) => { acc[resourceName] = props => ( - + Reading {resourceName}...

}> + +
) // Make sure the component is pretty printed in reconciler output @@ -17,5 +19,3 @@ const resourceComponents = Object.keys(resources).reduce( }, {} ) - -module.exports = resourceComponents diff --git a/packages/gatsby-recipes/src/renderer/resource-provider.js b/packages/gatsby-recipes/src/renderer/resource-provider.js new file mode 100644 index 0000000000000..c03441662184c --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/resource-provider.js @@ -0,0 +1,22 @@ +import React, { useContext } from "react" + +const ResourceContext = React.createContext([]) + +export const useResourceContext = () => { + const context = useContext(ResourceContext) + return context +} + +export const useResource = key => { + const context = useContext(ResourceContext) + const result = context.find(c => c.resourceDefinitions._key === key) + return result || {} +} + +export const useResourceByUUID = uuid => { + const context = useContext(ResourceContext) + const result = context.find(c => c._uuid === uuid) + return result +} + +export const ResourceProvider = ResourceContext.Provider diff --git a/packages/gatsby-recipes/src/renderer/step-component.js b/packages/gatsby-recipes/src/renderer/step-component.js new file mode 100644 index 0000000000000..3c04c8a1a9228 --- /dev/null +++ b/packages/gatsby-recipes/src/renderer/step-component.js @@ -0,0 +1,22 @@ +import React, { useContext } from "react" + +const StepContext = React.createContext({}) + +export const useRecipeStep = () => { + const context = useContext(StepContext) + return context +} + +export const StepProvider = ({ step, totalSteps, children }) => ( + + {children} + +) + +export const RecipeStep = ({ step, totalSteps, children }) => ( + + {children} + +) + +export const RecipeIntroduction = `div` diff --git a/packages/gatsby-recipes/src/renderer/transform-to-plan-structure.js b/packages/gatsby-recipes/src/renderer/transform-to-plan-structure.js index e1a0d9325eac4..06856857b6b5b 100644 --- a/packages/gatsby-recipes/src/renderer/transform-to-plan-structure.js +++ b/packages/gatsby-recipes/src/renderer/transform-to-plan-structure.js @@ -1,25 +1,70 @@ const providedResources = require(`../resources`) +const isResource = type => type && (type === `Input` || providedResources[type]) -const transform = (props = {}) => { +const extractPlan = (node, type) => { + if (!isResource(type)) { + return null + } + + let text = {} + if (node.text) { + try { + text = JSON.parse(node.text) + } catch { + return null + } + } + + const { _props: props, ...plan } = text + + return { + resourceName: type, + resourceDefinitions: props, + ...plan, + } +} + +const transform = (props = {}, type) => { if (!props.children) { - return [] + const plan = extractPlan(props, type) + return plan ? [plan] : [] } - const plan = props.children.reduce((acc, curr) => { - const childResourcePlans = transform(curr) + const plan = props.children.filter(Boolean).reduce((acc, curr) => { + const childType = curr.type || type + + let currText = {} + if (curr.text) { + try { + currText = JSON.parse(curr.text) + } catch {} // eslint-disable-line + } - if (!providedResources[curr.type]) { - return [...acc, ...childResourcePlans] + if (childType === `Input`) { + currText.resourceName = `Input` + return [...acc, currText] } - const { _props, ...plan } = JSON.parse(curr.children[0].text) + if (!providedResources[childType]) { + return [...acc, ...transform(curr, childType)] + } + + const [rawResource, ...resourceChildren] = curr.children || [] + const { _props, ...plan } = JSON.parse(rawResource.text) const resourcePlan = { - resourceName: curr.type, + resourceName: childType, resourceDefinitions: _props, ...plan, } + if (resourceChildren.length) { + resourcePlan.resourceChildren = transform( + { children: resourceChildren }, + childType + ) + } + return [...acc, resourcePlan] }, []) diff --git a/packages/gatsby-recipes/src/resources.js b/packages/gatsby-recipes/src/resources.js index 72ef0f29d43fe..bc3d6da92534e 100644 --- a/packages/gatsby-recipes/src/resources.js +++ b/packages/gatsby-recipes/src/resources.js @@ -7,6 +7,10 @@ const npmPackageResource = require(`./providers/npm/package`) const npmPackageScriptResource = require(`./providers/npm/script`) const npmPackageJsonResource = require(`./providers/npm/package-json`) const gitIgnoreResource = require(`./providers/git/ignore`) +const contentfulSpace = require(`./providers/contentful/space`) +const contentfulEnvironment = require(`./providers/contentful/environment`) +const contentfulType = require(`./providers/contentful/type`) +const contentfulEntry = require(`./providers/contentful/entry`) const componentResourceMapping = { File: fileResource, @@ -18,6 +22,10 @@ const componentResourceMapping = { NPMScript: npmPackageScriptResource, NPMPackageJson: npmPackageJsonResource, GitIgnore: gitIgnoreResource, + ContentfulSpace: contentfulSpace, + ContentfulEnvironment: contentfulEnvironment, + ContentfulType: contentfulType, + ContentfulEntry: contentfulEntry, } module.exports = componentResourceMapping diff --git a/packages/gatsby-recipes/src/transform-recipe-mdx.js b/packages/gatsby-recipes/src/transform-recipe-mdx.js new file mode 100644 index 0000000000000..a7afff82b5409 --- /dev/null +++ b/packages/gatsby-recipes/src/transform-recipe-mdx.js @@ -0,0 +1,59 @@ +import babelPluginTransformReactJsx from "@babel/plugin-transform-react-jsx" +import babelChainingPlugin from "@babel/plugin-proposal-optional-chaining" +import mdx from "@mdx-js/mdx" +import babelPluginRemoveShortcodes from "./renderer/babel-plugin-remove-shortcodes" +import babelPluginCopyKeyProp from "./renderer/babel-plugin-copy-key-prop" +import babelPluginMoveExportKeywords from "./renderer/babel-plugin-move-export-keywords" + +const mdxCache = new Map() +const jsxCache = new Map() + +const transformJsx = (jsx, isRenderer) => { + delete require.cache[require.resolve(`@babel/standalone`)] + const { transform } = require(`@babel/standalone`) + const options = { + parserOpts: { + // We want to return outside of a function because the output from + // Babel will be evaluated inline as part of the render process. + allowReturnOutsideFunction: true, + }, + plugins: [ + babelPluginCopyKeyProp, + babelPluginMoveExportKeywords, + // TODO figure out how to use preset-env + babelChainingPlugin, + [babelPluginTransformReactJsx, { useBuiltIns: true }], + ], + } + + if (isRenderer) { + options.plugins.push(babelPluginRemoveShortcodes) + } + + const { code } = transform(jsx, options) + + return code +} + +export default (mdxSrc, isRenderer = false) => { + let jsxFromMdx + if (mdxCache.has(mdxSrc)) { + jsxFromMdx = mdxCache.get(mdxSrc) + } else { + jsxFromMdx = mdx.sync(mdxSrc, { + skipExport: true, + commonmark: true, + }) + mdxCache.set(mdxSrc, jsxFromMdx) + } + + let srcCode + if (jsxCache.has(jsxFromMdx)) { + srcCode = jsxCache.get(jsxFromMdx) + } else { + srcCode = transformJsx(jsxFromMdx, isRenderer) + jsxCache.set(jsxFromMdx, srcCode) + } + + return srcCode +} diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 9ad56735e4729..983203674aa8a 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -219,7 +219,12 @@ "url": "git+https://github.com/gatsbyjs/gatsby.git" }, "resolutions": { - "graphql": "^14.6.0" + "graphql": "^14.6.0", + "@mdx-js/mdx": "^2.0.0-next.3", + "@mdx-js/react": "^2.0.0-next.3", + "@mdx-js/runtime": "^2.0.0-next.3", + "remark-mdx": "^2.0.0-next.3", + "remark-mdxjs": "^2.0.0-next.3" }, "scripts": { "build": "npm run build:types && npm run build:src && npm run build:internal-plugins && npm run build:rawfiles && npm run build:cjs", diff --git a/scripts/e2e-test.sh b/scripts/e2e-test.sh index debf03b636a25..52a96eea0a54e 100755 --- a/scripts/e2e-test.sh +++ b/scripts/e2e-test.sh @@ -9,7 +9,7 @@ command -v gatsby-dev || command -v sudo && sudo npm install -g gatsby-dev-cli | # setting up child integration test link to gatsby packages cd "$SRC_PATH" && gatsby-dev --set-path-to-repo "$GATSBY_PATH" && -gatsby-dev --scan-once --quiet && # copies _all_ files in gatsby/packages +gatsby-dev --scan-once && # copies _all_ files in gatsby/packages chmod +x ./node_modules/.bin/gatsby && # this is sometimes necessary to ensure executable sh -c "$CUSTOM_COMMAND" && echo "e2e test run succeeded" diff --git a/yarn.lock b/yarn.lock index e9915eb413fdb..9c6cfc300fddd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -120,6 +120,28 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.9.6": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.0.tgz#73b9c33f1658506887f767c26dae07798b30df76" + integrity sha512-mkLq8nwaXmDtFmRkQ8ED/eA2CnVw4zr7dCztKalZXBvdK5EeNUAesrrwUqjQEzFgomJssayzB0aqlOsP1vGLqg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.0" + "@babel/types" "^7.11.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@^7.0.0", "@babel/generator@^7.10.3", "@babel/generator@^7.10.4", "@babel/generator@^7.9.4", "@babel/generator@^7.9.6": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.4.tgz#e49eeed9fe114b62fa5b181856a43a5e32f5f243" @@ -139,6 +161,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" + integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ== + dependencies: + "@babel/types" "^7.11.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.10.1", "@babel/helper-annotate-as-pure@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" @@ -163,6 +194,15 @@ "@babel/helper-module-imports" "^7.10.1" "@babel/types" "^7.10.1" +"@babel/helper-builder-react-jsx-experimental@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.5.tgz#f35e956a19955ff08c1258e44a515a6d6248646b" + integrity sha512-Buewnx6M4ttG+NLkKyt7baQn7ScC/Td+e99G914fRU8fGIUivDDgVIQeDHFa5e4CRSJQt58WpNHhsAZgtzVhsg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/types" "^7.10.5" + "@babel/helper-builder-react-jsx@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.3.tgz#62c4b7bb381153a0a5f8d83189b94b9fb5384fc5" @@ -171,6 +211,14 @@ "@babel/helper-annotate-as-pure" "^7.10.1" "@babel/types" "^7.10.3" +"@babel/helper-builder-react-jsx@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d" + integrity sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-compilation-targets@^7.10.2": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" @@ -283,6 +331,19 @@ "@babel/types" "^7.10.5" lodash "^4.17.19" +"@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" + "@babel/helper-optimise-call-expression@^7.10.3", "@babel/helper-optimise-call-expression@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" @@ -336,6 +397,13 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-skip-transparent-expression-wrappers@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" + integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q== + dependencies: + "@babel/types" "^7.11.0" + "@babel/helper-split-export-declaration@^7.10.1", "@babel/helper-split-export-declaration@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz#2c70576eaa3b5609b24cb99db2888cc3fc4251d1" @@ -343,6 +411,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + "@babel/helper-validator-identifier@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" @@ -414,6 +489,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== +"@babel/parser@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.0.tgz#a9d7e11aead25d3b422d17b2c6502c8dddef6a5d" + integrity sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw== + "@babel/plugin-proposal-async-generator-functions@^7.10.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.4.tgz#4b65abb3d9bacc6c657aaa413e56696f9f170fc6" @@ -563,6 +643,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-chaining" "^7.8.0" +"@babel/plugin-proposal-optional-chaining@^7.9.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" + integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-proposal-pipeline-operator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-pipeline-operator/-/plugin-proposal-pipeline-operator-7.8.3.tgz#c3569228e7466f91bfff7f1c1ae18fb5d36b3097" @@ -679,7 +768,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@7.10.4": +"@babel/plugin-syntax-jsx@7.10.4", "@babel/plugin-syntax-jsx@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c" integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g== @@ -995,7 +1084,7 @@ "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-syntax-jsx" "^7.10.1" -"@babel/plugin-transform-react-jsx@^7.10.1", "@babel/plugin-transform-react-jsx@^7.10.3", "@babel/plugin-transform-react-jsx@^7.3.0": +"@babel/plugin-transform-react-jsx@^7.10.1", "@babel/plugin-transform-react-jsx@^7.3.0": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.3.tgz#c07ad86b7c159287c89b643f201f59536231048e" integrity sha512-Y21E3rZmWICRJnvbGVmDLDZ8HfNDIwjGF3DXYHx1le0v0mIHCs0Gv5SavyW5Z/jgAHLaAoJPiwt+Dr7/zZKcOQ== @@ -1005,6 +1094,16 @@ "@babel/helper-plugin-utils" "^7.10.3" "@babel/plugin-syntax-jsx" "^7.10.1" +"@babel/plugin-transform-react-jsx@^7.9.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" + integrity sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A== + dependencies: + "@babel/helper-builder-react-jsx" "^7.10.4" + "@babel/helper-builder-react-jsx-experimental" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" + "@babel/plugin-transform-react-pure-annotations@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz#f5e7c755d3e7614d4c926e144f501648a5277b70" @@ -1027,6 +1126,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-runtime@^7.0.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz#e27f78eb36f19448636e05c33c90fd9ad9b8bccf" + integrity sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + resolve "^1.8.1" + semver "^5.5.1" + "@babel/plugin-transform-runtime@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.3.tgz#3b287b06acc534a7cb6e6c71d6b1d88b1922dd6c" @@ -1254,10 +1363,10 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/standalone@^7.10.3": - version "7.10.3" - resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.10.3.tgz#aaabbf0fcfc82d595d3e1b9467b201d1a33a6e64" - integrity sha512-pGqfFo2VXa/Ei40T9kSpe8i87MzsOfAQJMpHRABTAjLZrH5IwskSqfAzBP5o2VJbpA4S34LMxOTZ26lirCCVFQ== +"@babel/standalone@^7.10.2": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.11.0.tgz#c8882afdb737306cb127f58f0befec4def31beb5" + integrity sha512-Cf4WggQ4vPsGsVU8DbbWtzI/AEQ/oyYE6XFqGnAAadBT5kro0YfDfJhtcLdtye9C6RkOfV0DxcQ3eoZ1Bv6yrA== "@babel/template@^7.0.0", "@babel/template@^7.10.1", "@babel/template@^7.10.3", "@babel/template@^7.10.4", "@babel/template@^7.8.6": version "7.10.4" @@ -1313,6 +1422,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24" + integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.0" + "@babel/types" "^7.11.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.10.1", "@babel/types@^7.10.3", "@babel/types@^7.10.4", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.9.0", "@babel/types@^7.9.6": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.4.tgz#369517188352e18219981efd156bfdb199fff1ee" @@ -1331,6 +1455,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" + integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" @@ -1371,7 +1504,7 @@ "@emotion/utils" "0.11.3" "@emotion/weak-memoize" "0.2.5" -"@emotion/core@^10.0.0", "@emotion/core@^10.0.28": +"@emotion/core@^10.0.0", "@emotion/core@^10.0.14", "@emotion/core@^10.0.28": version "10.0.28" resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.28.tgz#bb65af7262a234593a9e952c041d0f1c9b9bef3d" integrity sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA== @@ -1435,7 +1568,7 @@ "@emotion/serialize" "^0.11.15" "@emotion/utils" "0.11.3" -"@emotion/styled@*", "@emotion/styled@^10.0.0", "@emotion/styled@^10.0.27": +"@emotion/styled@*", "@emotion/styled@^10.0.0", "@emotion/styled@^10.0.14", "@emotion/styled@^10.0.27": version "10.0.27" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf" integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q== @@ -1566,18 +1699,18 @@ "@graphql-tools/utils" "6.0.9" tslib "~2.0.0" -"@graphql-tools/schema@^6.0.11": - version "6.0.11" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-6.0.11.tgz#4623c3662a8843822e36139855504ef8d7d89088" - integrity sha512-Zl9LTwOnkMaNtgs1+LJEYtklywtn602kRbxkRFeA7nFGaDmFPFHZnfQqcLsfhaPA8S0jNCQnbucHERCz8pRUYA== +"@graphql-tools/schema@^6.0.14": + version "6.0.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-6.0.15.tgz#b016f9f36820342982887a291baa7e7d11b039ae" + integrity sha512-Wo+d6/OPjeXjwB1pcqsWmqLdweGH+BVhvKe/YPQA/uiWr8ikgShvNLNiuF03gc/1AMR487A09XcPEyabRKJLew== dependencies: - "@graphql-tools/utils" "6.0.11" + "@graphql-tools/utils" "6.0.15" tslib "~2.0.0" -"@graphql-tools/utils@6.0.11", "@graphql-tools/utils@^6.0.11": - version "6.0.11" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.0.11.tgz#c394201c992dbc8f3644b9ad71411fdd55832f2d" - integrity sha512-BK6HO73FbB/Ufac6XX5H0O2q4tEZi//HaQ7DgmHFoda53GZSZ/ZckJ59wh/tUvHykEaSFUSmMBVQxKbXBhGhyg== +"@graphql-tools/utils@6.0.15", "@graphql-tools/utils@^6.0.14": + version "6.0.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.0.15.tgz#6d54d383285bea3c22797531933b62a408e78e49" + integrity sha512-VG5cMLPgh9RDLGHamGpXVnBrNw7bZGT46LrxK7IIqDZI9H0GPsRCo8+p+CfDkw0IlDiEECb624WVCpm9IYNecA== dependencies: "@ardatan/aggregate-error" "0.0.1" camel-case "4.1.1" @@ -1619,7 +1752,7 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-6.2.4.tgz#4b95fbaccbfba90185690890bdf1a2fbbda10595" integrity sha512-HOJ20Kc93DkDVvjwHyHawPwPkX44sIrbXazAUDiUXaY2R9JwQGo2PhFfnQtdrsIe4igjG2fPgMra7NYw7qhy0A== -"@hapi/hoek@8.x", "@hapi/hoek@8.x.x": +"@hapi/hoek@8.x.x": version "8.5.1" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== @@ -2799,32 +2932,34 @@ unist-builder "2.0.3" unist-util-visit "2.0.3" -"@mdx-js/mdx@^1.6.6": - version "1.6.6" - resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.6.tgz#6e235f0ca47c8652f4c744cf7bc46a1015bcaeaa" - integrity sha512-Q1j/RtjNbRZRC/ciaOqQLplsJ9lb0jJhDSvkusmzCsCX+NZH7YTUvccWf7l6zKW1CAiofJfqZdZtXkeJUDZiMw== +"@mdx-js/mdx@^2.0.0-next.4", "@mdx-js/mdx@^2.0.0-next.7": + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-2.0.0-next.7.tgz#3f27449e6a78ac0a9cc682f98e1c93442e93f166" + integrity sha512-GcdHQ+YTlIaNpsMPlw32kEp+GCrb+2GLeDDf2AFtJiRoTelgCinjYp1twxY42WF6A4K80ZYgpr0/A6PDQbKNyw== dependencies: - "@babel/core" "7.9.6" - "@babel/plugin-syntax-jsx" "7.8.3" + "@babel/core" "7.10.5" + "@babel/plugin-syntax-jsx" "7.10.4" "@babel/plugin-syntax-object-rest-spread" "7.8.3" - "@mdx-js/util" "^1.6.6" - babel-plugin-apply-mdx-type-prop "^1.6.6" - babel-plugin-extract-import-names "^1.6.6" + "@mdx-js/util" "^2.0.0-next.7" + babel-plugin-apply-mdx-type-prop "^2.0.0-next.7" + babel-plugin-extract-export-names "^2.0.0-next.7" + babel-plugin-extract-import-names "^2.0.0-next.7" camelcase-css "2.0.1" detab "2.0.3" - hast-util-raw "5.0.2" + hast-to-hyperscript "9.0.0" + hast-util-raw "6.0.0" lodash.uniq "4.5.0" mdast-util-to-hast "9.1.0" remark-footnotes "1.0.0" - remark-mdx "^1.6.6" + remark-mdx "^2.0.0-next.7" + remark-mdxjs "^2.0.0-next.7" remark-parse "8.0.2" remark-squeeze-paragraphs "4.0.0" - style-to-object "0.3.0" unified "9.0.0" unist-builder "2.0.3" - unist-util-visit "2.0.2" + unist-util-visit "2.0.3" -"@mdx-js/react@^1.0.0", "@mdx-js/react@^1.5.2", "@mdx-js/react@^1.6.6": +"@mdx-js/react@^1.0.0", "@mdx-js/react@^1.5.2": version "1.6.6" resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.6.tgz#71ece2a24261eed0e184c0ef9814fcb77b1a4aee" integrity sha512-zOOdNreHUNSFQ0dg3wYYg9sOGg2csf7Sk8JGBigeBq+4Xk4LO0QdycGAmgKNfeme+SyBV5LBIPjt1NNsScyWEQ== @@ -2834,13 +2969,18 @@ resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.16.tgz#538eb14473194d0b3c54020cb230e426174315cd" integrity sha512-+FhuSVOPo7+4fZaRwWuCSRUcZkJOkZu0rfAbBKvoCg1LWb1Td8Vzi0DTLORdSvgWNbU6+EL40HIgwTOs00x2Jw== -"@mdx-js/runtime@^1.6.6": - version "1.6.6" - resolved "https://registry.yarnpkg.com/@mdx-js/runtime/-/runtime-1.6.6.tgz#2100a4da17f18dcbc9b59bf279b0f064df0a1377" - integrity sha512-JNt/dqIFMQ87Gp6oIZUn4oJZQOOk0rqc7JkIv/xYyTtUMaNCXWZ/ylRLoFbQCyfONxayOz4w4e8Bd4ruxScOGQ== +"@mdx-js/react@^2.0.0-next.4", "@mdx-js/react@^2.0.0-next.7": + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.0.0-next.7.tgz#33d3a2a961d5f2ebf36d096642c2a306111feae4" + integrity sha512-VugV3o0zOD6pABtQEDDWNxiU8f+tS4KMiOgnwNiyxxOEwEZgBnXfMhZYDtHfrnhHxS59ValJ5zITnbdBwPbJkA== + +"@mdx-js/runtime@^2.0.0-next.4": + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/@mdx-js/runtime/-/runtime-2.0.0-next.7.tgz#6efd063bc9bec85a16e38fddab02d12812c05d43" + integrity sha512-+Nnjjs1LsdxkkdLODnFE0gz0B69uYJ+eUxScUBDoOY61jnm1NTvq/Axu3/Ax4YMtNwVCQMqBXXeOgLBDku1ISg== dependencies: - "@mdx-js/mdx" "^1.6.6" - "@mdx-js/react" "^1.6.6" + "@mdx-js/mdx" "^2.0.0-next.7" + "@mdx-js/react" "^2.0.0-next.7" buble-jsx-only "^0.19.8" "@mdx-js/util@1.6.16": @@ -2853,6 +2993,11 @@ resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.6.tgz#9c70eb7e7e4abc1083c8edf7151d35a19e442c00" integrity sha512-PKTHVgMHnK5p+kcMWWNnZuoR7O19VmHiOujmVcyN50hya7qIdDb5vvsYC+dwLxApEXiABhLozq0dlIwFeS3yjg== +"@mdx-js/util@^2.0.0-next.7": + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-2.0.0-next.7.tgz#b1c52e7622917d7601b2c9ba2f132aaf8d4224fc" + integrity sha512-gsid2rh63B7/U1gPLXz9N5bfWR+n5GYxAcVCJDf8H+XfCC7NHsEX9ZHL9IdmXndOPT4ZTSW6V/jD8VeQdvnzLQ== + "@mikaelkristiansson/domready@^1.0.10": version "1.0.10" resolved "https://registry.yarnpkg.com/@mikaelkristiansson/domready/-/domready-1.0.10.tgz#f6d69866c0857664e70690d7a0bfedb72143adb5" @@ -3152,6 +3297,14 @@ "@babel/helper-module-imports" "^7.7.4" "@rollup/pluginutils" "^3.0.8" +"@rollup/plugin-babel@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.1.0.tgz#ad8b5803fa6e1feb0f168984edc040b90d966450" + integrity sha512-zXBEYmfiLAMvB+ZBa6m/q9hsQYAq1sUFdjuP1F6C2pf6uQcpHwAWQveZgzS63zXdKPUYHD3Dr7BhjCqcr0bbLw== + dependencies: + "@babel/helper-module-imports" "^7.7.4" + "@rollup/pluginutils" "^3.0.8" + "@rollup/plugin-commonjs@^13.0.0": version "13.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-13.0.0.tgz#8a1d684ba6848afe8b9e3d85649d4b2f6f7217ec" @@ -3165,6 +3318,19 @@ magic-string "^0.25.2" resolve "^1.11.0" +"@rollup/plugin-commonjs@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz#4285f9ec2db686a31129e5a2b415c94aa1f836f0" + integrity sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + commondir "^1.0.1" + estree-walker "^1.0.1" + glob "^7.1.2" + is-reference "^1.1.2" + magic-string "^0.25.2" + resolve "^1.11.0" + "@rollup/plugin-json@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" @@ -3183,6 +3349,19 @@ is-module "^1.0.0" resolve "^1.11.1" +"@rollup/plugin-node-resolve@^8.4.0": + version "8.4.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz#261d79a680e9dc3d86761c14462f24126ba83575" + integrity sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deep-freeze "^0.0.1" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.17.0" + "@rollup/pluginutils@^3.0.0", "@rollup/pluginutils@^3.0.8": version "3.0.10" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.0.10.tgz#a659b9025920378494cd8f8c59fbf9b3a50d5f12" @@ -3192,6 +3371,15 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + "@rtsao/csstype@2.6.5-forked.0": version "2.6.5-forked.0" resolved "https://registry.yarnpkg.com/@rtsao/csstype/-/csstype-2.6.5-forked.0.tgz#b5b4e2a07ad83a91874ebf5fabdb73dc8c1632f6" @@ -3854,6 +4042,14 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/react@^16.9.38": + version "16.9.43" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.43.tgz#c287f23f6189666ee3bebc2eb8d0f84bcb6cdb6b" + integrity sha512-PxshAFcnJqIWYpJbLPriClH53Z2WlJcVZE+NP2etUtWQs2s7yIMj3/LDKZT/5CHJ/F62iyjVCDu2H3jHEXIxSg== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/reflexbox@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/reflexbox/-/reflexbox-4.0.1.tgz#dfe91aace3c931766507cfd1cce65498a4d052a0" @@ -3870,6 +4066,13 @@ dependencies: "@types/node" "*" +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + "@types/rimraf@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.3.tgz#0199a46af106729ba14213fda7b981278d8c84f2" @@ -4069,6 +4272,11 @@ dependencies: "@types/node" "*" +"@types/yoga-layout@1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a" + integrity sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw== + "@typescript-eslint/eslint-plugin@^2.24.0", "@typescript-eslint/eslint-plugin@^2.28.0": version "2.28.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.28.0.tgz#4431bc6d3af41903e5255770703d4e55a0ccbdec" @@ -4119,6 +4327,13 @@ dependencies: wonka "^4.0.14" +"@urql/core@^1.12.3": + version "1.12.3" + resolved "https://registry.yarnpkg.com/@urql/core/-/core-1.12.3.tgz#74f7b8e073cf706380bb3dd28484b8136cc96905" + integrity sha512-e4IXXQ4nes3KyusgYV925DuzfDAfo4ex7Ls3tZfOExkxEcXh0i0XnizXp0rvZmWRch69YCkc+Lh1Gy2aY49HTQ== + dependencies: + wonka "^4.0.14" + "@verdaccio/commons-api@9.6.1", "@verdaccio/commons-api@^9.6.1": version "9.6.1" resolved "https://registry.yarnpkg.com/@verdaccio/commons-api/-/commons-api-9.6.1.tgz#f62d1e0d7e55f19c483989f718ed2df2bf55ae3a" @@ -4440,7 +4655,7 @@ acorn@^6.0.1, acorn@^6.0.4, acorn@^6.0.7, acorn@^6.1.1, acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.0.0, acorn@^7.1.0, acorn@^7.3.1: +acorn@^7.0.0, acorn@^7.1.0, acorn@^7.2.0: version "7.3.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== @@ -5165,7 +5380,7 @@ atob@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" -auto-bind@^4.0.0: +auto-bind@4.0.0, auto-bind@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== @@ -5331,13 +5546,13 @@ babel-plugin-apply-mdx-type-prop@1.6.16: "@babel/helper-plugin-utils" "7.10.4" "@mdx-js/util" "1.6.16" -babel-plugin-apply-mdx-type-prop@^1.6.6: - version "1.6.6" - resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.6.tgz#f72d7ff9f40620c51280a1acb4964c55bc07ba02" - integrity sha512-rUzVvkQa8/9M63OZT6qQQ1bS8P0ozhXp9e5uJ3RwRJF5Me7s4nZK5SYhyNHYc0BkAflWnCOGMP3oPQUfuyB8tg== +babel-plugin-apply-mdx-type-prop@^2.0.0-next.7: + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-2.0.0-next.7.tgz#6a2eeac3b0c281515c69cbc08d5163856e288e50" + integrity sha512-JhT3sMNjNRzrMxpgkVUN5s3UvDpDCcUTsqsgZvIC2OXtQqNR8ZJxMHckbAJRWmz0YqyuVbFgLUQKpDGHAAB6GA== dependencies: - "@babel/helper-plugin-utils" "7.8.3" - "@mdx-js/util" "^1.6.6" + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "^2.0.0-next.7" babel-plugin-codegen@^4.0.0: version "4.0.1" @@ -5376,6 +5591,13 @@ babel-plugin-emotion@^10.0.27: find-root "^1.1.0" source-map "^0.5.7" +babel-plugin-extract-export-names@^2.0.0-next.7: + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-export-names/-/babel-plugin-extract-export-names-2.0.0-next.7.tgz#26be2512f8535a92c47fc727ce2de873313658cd" + integrity sha512-CTsKh5l99oBd5jemej5BHdzxwaXDYNi3zryGEHaCcO6h3H6OAx7sQyHV76yO7eWHXFBc+t30YSuubpwJQwLHhg== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + babel-plugin-extract-import-names@1.6.16: version "1.6.16" resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.16.tgz#b964004e794bdd62534c525db67d9e890d5cc079" @@ -5383,12 +5605,12 @@ babel-plugin-extract-import-names@1.6.16: dependencies: "@babel/helper-plugin-utils" "7.10.4" -babel-plugin-extract-import-names@^1.6.6: - version "1.6.6" - resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.6.tgz#70e39a46f1b2a08fbd061336a322d1ddd81a2f44" - integrity sha512-UtMuiQJnhVPAGE2+pDe7Nc9NVEmDdqGTN74BtRALgH+7oag88RpxFLOSiA+u5mFkFg741wW9Ut5KiyJpksEj/g== +babel-plugin-extract-import-names@^2.0.0-next.7: + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-2.0.0-next.7.tgz#e55b24fd2c86b2a7e55af279953c4a1bb6bc7b60" + integrity sha512-WSYLKKC9a3nLbfnrrbXoEeC8LS3jCn1wBWOcc4Tlwl7n97EBuvCEEMQCHnV7rEDQFl9impbAKr9kLH0QEa8IXg== dependencies: - "@babel/helper-plugin-utils" "7.8.3" + "@babel/helper-plugin-utils" "7.10.4" babel-plugin-istanbul@^4.1.6: version "4.1.6" @@ -5465,6 +5687,11 @@ babel-plugin-react-css-modules@^3.4.2: postcss-modules-scope "^1.1.0" postcss-modules-values "^1.3.0" +babel-plugin-remove-export-keywords@^1.6.5: + version "1.6.16" + resolved "https://registry.yarnpkg.com/babel-plugin-remove-export-keywords/-/babel-plugin-remove-export-keywords-1.6.16.tgz#e764b42e3c8e4a5ce3e2c996dc43b6348d5d94cf" + integrity sha512-JrB9ZASlMAfkRF+5NdgoQxgenhJxzXFEO1vrqsSDJdzLrC38L2wrvXF9mm1YLbrehkZxcrNz9UYDyARP4jaY9g== + babel-plugin-syntax-jsx@^6.18.0: version "6.18.0" resolved "http://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" @@ -6131,6 +6358,13 @@ builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" +builtins@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-2.0.1.tgz#42a4d6fe38973a7c185b435970d13e5e70f70f3c" + integrity sha512-XkkVe5QAb6guWPXTzpSrYpSlN3nqEmrrE2TkAr/tp7idSF6+MONh9WvKrAuR3HiKLvoSgmbs8l1U9IPmMrIoLw== + dependencies: + semver "^6.0.0" + bunyan@1.8.13: version "1.8.13" resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.13.tgz#dde6bacd9ebccaedb110f1319f88db3f415ccfeb" @@ -6440,6 +6674,14 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + change-case@^3.0.1, change-case@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e" @@ -6676,6 +6918,11 @@ cli-spinners@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" +cli-spinners@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" + integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== + cli-table3@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -6815,6 +7062,13 @@ coa@~1.0.1: dependencies: q "^1.1.2" +code-excerpt@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-3.0.0.tgz#fcfb6748c03dba8431c19f5474747fad3f250f10" + integrity sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw== + dependencies: + convert-to-spaces "^1.0.1" + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -7087,6 +7341,21 @@ concat-with-sourcemaps@^1.1.0: dependencies: source-map "^0.6.1" +concurrently@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-5.2.0.tgz#ead55121d08a0fc817085584c123cedec2e08975" + integrity sha512-XxcDbQ4/43d6CxR7+iV8IZXhur4KbmEJk1CetVMUqCy34z9l0DkszbY+/9wvmSnToTej0SYomc2WSRH+L0zVJw== + dependencies: + chalk "^2.4.2" + date-fns "^2.0.1" + lodash "^4.17.15" + read-pkg "^4.0.1" + rxjs "^6.5.2" + spawn-command "^0.0.2-1" + supports-color "^6.1.0" + tree-kill "^1.2.2" + yargs "^13.3.0" + config-chain@^1.1.11: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" @@ -7188,6 +7457,16 @@ content-type@^1.0.4, content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" +contentful-management@^5.26.3: + version "5.28.0" + resolved "https://registry.yarnpkg.com/contentful-management/-/contentful-management-5.28.0.tgz#f3b58d60400d66e42439bbd9085cecb0e486f0bb" + integrity sha512-o+qihN3zrD6+/BT/e8n26jl/zQvmV6+9S6NY5QDmzM+IaiSeCk6yvPMq74s+IZT9mOS54igl6qFTbeIpdJ9FDA== + dependencies: + axios "^0.19.0" + contentful-sdk-core "^6.4.0" + lodash "^4.17.11" + type-fest "0.15.1" + contentful-resolve-response@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/contentful-resolve-response/-/contentful-resolve-response-1.2.2.tgz#3f83a5f4742854de740e250a11020b4fb646da91" @@ -7195,7 +7474,7 @@ contentful-resolve-response@^1.2.2: dependencies: lodash "^4.17.15" -contentful-sdk-core@^6.4.5: +contentful-sdk-core@^6.4.0, contentful-sdk-core@^6.4.5: version "6.4.5" resolved "https://registry.yarnpkg.com/contentful-sdk-core/-/contentful-sdk-core-6.4.5.tgz#e73f4c5426f354608543fc73e46c17c6730180e9" integrity sha512-rygNuiwbG6UKrJg6EDlaKewayTeLWrjA2wJwVmq7rV/DYo0cic6t28y0EMhRQ4pgJDV5HyUQFoFeBm2lwLfG2Q== @@ -7320,6 +7599,11 @@ convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, dependencies: safe-buffer "~5.1.1" +convert-to-spaces@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz#7e3e48bbe6d997b1417ddca2868204b4d3d85715" + integrity sha1-fj5Iu+bZl7FBfdyihoIEtNPYVxU= + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -8011,6 +8295,11 @@ date-fns@^1.27.2, date-fns@^1.30.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== +date-fns@^2.0.1: + version "2.15.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.15.0.tgz#424de6b3778e4e69d3ff27046ec136af58ae5d5f" + integrity sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ== + date-fns@^2.14.0, date-fns@^2.8.1: version "2.14.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.14.0.tgz#359a87a265bb34ef2e38f93ecf63ac453f9bc7ba" @@ -8159,6 +8448,11 @@ deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" +deep-freeze@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" + integrity sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ= + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -9175,6 +9469,11 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -9638,6 +9937,11 @@ eventemitter3@^4.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== +eventemitter3@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + events@^1.1.0, events@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -10110,6 +10414,13 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -10380,6 +10691,11 @@ flatted@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" +flatted@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.0.4.tgz#f75ed043c803c8ca43a4d0bbc05f2bf20b5d9656" + integrity sha512-4gZhsMc26tSiMgQ+0gRN818ST2KCkX/4EvqocCkE1+SRb7mapNk4KLSP+XAj02jc8rxuyD3DrmI3a0BQ/TNOpg== + flow-parser@0.*: version "0.105.2" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.105.2.tgz#7ba3ab7718c0519979c2f061e89c7b3c3624ee96" @@ -10523,6 +10839,20 @@ format@^0.2.0, format@^0.2.2: resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= +formik@^2.0.8: + version "2.1.5" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.1.5.tgz#de5bbbe35543fa6d049fe96b8ee329d6cd6892b8" + integrity sha512-bWpo3PiqVDYslvrRjTq0Isrm0mFXHiO33D8MS6t6dWcqSFGeYF52nlpCM2xwOJ6tRVRznDkL+zz/iHPL4LDuvQ== + dependencies: + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.14" + lodash-es "^4.17.14" + react-fast-compare "^2.0.1" + scheduler "^0.18.0" + tiny-warning "^1.0.2" + tslib "^1.10.0" + formik@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/formik/-/formik-2.1.4.tgz#8deef07ec845ea98f75e03da4aad7aab4ac46570" @@ -10722,6 +11052,26 @@ gatsby-interface@0.0.183: lodash.sample "^4.2.1" theme-ui "^0.2.49" +gatsby-interface@^0.0.166: + version "0.0.166" + resolved "https://registry.yarnpkg.com/gatsby-interface/-/gatsby-interface-0.0.166.tgz#ce970498fa0b36767595d423a30ed16a4832ac1e" + integrity sha512-PN0lTVOKu50zfY7kfjgHvT5jsYZIOdSxuWrV/WVxDXo4O3oifLiWUyfFy8zg9T8S1G+TwRyfzhWT9Pfj1CZ2Dg== + dependencies: + "@mdx-js/react" "^1.5.2" + "@reach/alert" "0.10.3" + "@reach/combobox" "0.10.3" + "@reach/dialog" "0.10.3" + "@reach/menu-button" "0.10.3" + "@reach/popover" "0.10.3" + "@reach/tabs" "0.10.3" + "@reach/tooltip" "0.10.3" + "@types/lodash.sample" "^4.2.6" + case "^1.6.2" + date-fns "^2.8.1" + gatsby-design-tokens "^2.0.2" + lodash.sample "^4.2.1" + theme-ui "^0.2.49" + gatsby-node-helpers@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/gatsby-node-helpers/-/gatsby-node-helpers-0.3.0.tgz#3bdca3b7902a702a5834fef280ad66d51099d57c" @@ -11746,30 +12096,7 @@ hasha@^5.2.0: is-stream "^2.0.0" type-fest "^0.8.0" -hast-to-hyperscript@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-5.0.0.tgz#5106cbba78edb7c95e2e8a49079371eb196c1ced" - dependencies: - comma-separated-tokens "^1.0.0" - property-information "^4.0.0" - space-separated-tokens "^1.0.0" - style-to-object "^0.2.1" - unist-util-is "^2.0.0" - web-namespaces "^1.1.2" - -hast-to-hyperscript@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-7.0.2.tgz#e9237c88c6069999ad38aec847fefc296f484c4c" - integrity sha512-NBMMst0hkDR21uSH75m9W2DkljBrLoMQEhGiLMLNij4HIzEDJMC1UG+CFR6EAjHi2zs3NHBoaAHJOHxftoIN2g== - dependencies: - comma-separated-tokens "^1.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" - style-to-object "^0.2.1" - unist-util-is "^3.0.0" - web-namespaces "^1.1.2" - -hast-to-hyperscript@^9.0.0: +hast-to-hyperscript@9.0.0, hast-to-hyperscript@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.0.tgz#768fb557765fe28749169c885056417342d71e83" integrity sha512-NJvMYU3GlMLs7hN3CRbsNlMzusVNkYBogVWDGybsuuVQ336gFLiD+q9qtFZT2meSHzln3pNISZWTASWothMSMg== @@ -11782,6 +12109,17 @@ hast-to-hyperscript@^9.0.0: unist-util-is "^4.0.0" web-namespaces "^1.0.0" +hast-to-hyperscript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-5.0.0.tgz#5106cbba78edb7c95e2e8a49079371eb196c1ced" + dependencies: + comma-separated-tokens "^1.0.0" + property-information "^4.0.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.2.1" + unist-util-is "^2.0.0" + web-namespaces "^1.1.2" + hast-util-from-parse5@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-4.0.2.tgz#b7164a7ffc88da4f751dc7c2f801ff8d7c143bab" @@ -11792,17 +12130,6 @@ hast-util-from-parse5@^4.0.2: web-namespaces "^1.1.2" xtend "^4.0.1" -hast-util-from-parse5@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.1.tgz#7da8841d707dcf7be73715f7f3b14e021c4e469a" - integrity sha512-UfPzdl6fbxGAxqGYNThRUhRlDYY7sXu6XU9nQeX4fFZtV+IHbyEJtd+DUuwOqNV4z3K05E/1rIkoVr/JHmeWWA== - dependencies: - ccount "^1.0.3" - hastscript "^5.0.0" - property-information "^5.0.0" - web-namespaces "^1.1.2" - xtend "^4.0.1" - hast-util-from-parse5@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.0.tgz#b38793c81e1a99f5fd592a4a88fc2731dccd0f30" @@ -11823,20 +12150,6 @@ hast-util-parse-selector@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.0.tgz#2175f18cdd697308fc3431d5c29a9e48dfa4817a" -hast-util-raw@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-5.0.2.tgz#62288f311ec2f35e066a30d5e0277f963ad43a67" - integrity sha512-3ReYQcIHmzSgMq8UrDZHFL0oGlbuVGdLKs8s/Fe8BfHFAyZDrdv1fy/AGn+Fim8ZuvAHcJ61NQhVMtyfHviT/g== - dependencies: - hast-util-from-parse5 "^5.0.0" - hast-util-to-parse5 "^5.0.0" - html-void-elements "^1.0.0" - parse5 "^5.0.0" - unist-util-position "^3.0.0" - web-namespaces "^1.0.0" - xtend "^4.0.0" - zwitch "^1.0.0" - hast-util-raw@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.0.tgz#49a38f5107d483f83a139709f2f705f22e7e7d32" @@ -11913,17 +12226,6 @@ hast-util-to-parse5@^4.0.1: xtend "^4.0.1" zwitch "^1.0.0" -hast-util-to-parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-5.1.1.tgz#cabf2dbe9ed988a5128fc708457b37cdf535a2e8" - integrity sha512-ivCeAd5FCXr7bapJIVsWMnx/EmbjkkW2TU2hd1prq+jGwiaUoK+FcpjyPNwsC5ogzCwWO669tOqIovGeLc/ntg== - dependencies: - hast-to-hyperscript "^7.0.0" - property-information "^5.0.0" - web-namespaces "^1.0.0" - xtend "^4.0.1" - zwitch "^1.0.0" - hast-util-to-parse5@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" @@ -12610,31 +12912,15 @@ init-package-json@^1.10.3: validate-npm-package-license "^3.0.1" validate-npm-package-name "^3.0.0" -ink-box@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ink-box/-/ink-box-1.0.0.tgz#8cbcb5541d32787d08d43acf1a9907e86e3572f3" - integrity sha512-wD2ldWX9lcE/6+flKbAJ0TZF7gKbTH8CRdhEor6DD8d+V0hPITrrGeST2reDBpCia8wiqHrdxrqTyafwtmVanA== - dependencies: - boxen "^3.0.0" - prop-types "^15.7.2" - -ink-link@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ink-link/-/ink-link-1.1.0.tgz#e00bd68dfd163a9392baecc0808391fd07e6cfbb" - integrity sha512-a716nYz4YDPu8UOA2PwabTZgTvZa3SYB/70yeXVmTOKFAEdMbJyGSVeNuB7P+aM2olzDj9AGVchA7W5QytF9uA== - dependencies: - prop-types "^15.7.2" - terminal-link "^2.1.1" - -ink-select-input@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/ink-select-input/-/ink-select-input-3.1.2.tgz#fd53f2f0946bc43989899522b013a2c10a60f722" - integrity sha512-PaLraGx8A54GhSkTNzZI8bgY0elAoa1jSPPe5Q52B5VutcBoJc4HE3ICDwsEGJ88l1Hw6AWjpeoqrq82a8uQPA== +ink-select-input@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ink-select-input/-/ink-select-input-4.0.0.tgz#ff642302b4ffea53227e96fa9152709de8a3de54" + integrity sha512-LwvsWqJh64ARY/4WxhC9B0+Dcb+BiTm9lzwTW5XH8OT5ng7drs7J7C23ZQdt25ZETCPvzg5BSZf2w6fhdg/SXw== dependencies: arr-rotate "^1.0.0" - figures "^2.0.0" + figures "^3.2.0" + lodash "^4.17.19" lodash.isequal "^4.5.0" - prop-types "^15.5.10" ink-spinner@^3.1.0: version "3.1.0" @@ -12644,6 +12930,14 @@ ink-spinner@^3.1.0: cli-spinners "^1.0.0" prop-types "^15.5.10" +ink-spinner@^4.0.0-0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ink-spinner/-/ink-spinner-4.0.0.tgz#a233df86e17b76a28ba5c76c412b30d43ef96fa7" + integrity sha512-Oq2u5SY1qZ61LbzyQReJxpoRmruLcWw3lRb7cq8t3fj9484Eo5HggayyX5OVEcCGURWyyH8kJmFZJgrSGtJqkA== + dependencies: + "@types/react" "^16.9.38" + cli-spinners "^2.3.0" + ink@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/ink/-/ink-2.7.1.tgz#ff1c75b4b022924e2993af62297fa0e48e85618b" @@ -12668,6 +12962,35 @@ ink@^2.7.1: wrap-ansi "^6.2.0" yoga-layout-prebuilt "^1.9.3" +ink@next: + version "3.0.0-7" + resolved "https://registry.yarnpkg.com/ink/-/ink-3.0.0-7.tgz#e64a7f8ca462f1d07bc371906871c3c2b24c141c" + integrity sha512-Faw3KSntkySygliO5auyn6F1QfuT4lxc77zssuTNcW4I7AL1w1UxZJrhAE6NwaRFLxdHG8eEcvRXjr7V/ZmHmg== + dependencies: + ansi-escapes "^4.2.1" + auto-bind "4.0.0" + chalk "^4.1.0" + cli-boxes "^2.2.0" + cli-cursor "^3.1.0" + cli-truncate "^2.1.0" + code-excerpt "^3.0.0" + indent-string "^4.0.0" + is-ci "^2.0.0" + lodash.throttle "^4.1.1" + patch-console "^1.0.0" + react-devtools-core "^4.6.0" + react-reconciler "^0.24.0" + scheduler "^0.18.0" + signal-exit "^3.0.2" + slice-ansi "^3.0.0" + stack-utils "^2.0.2" + string-length "^3.1.0" + type-fest "^0.12.0" + widest-line "^3.1.0" + wrap-ansi "^6.2.0" + ws "^7.2.5" + yoga-layout-prebuilt "^1.9.6" + inline-style-parser@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" @@ -13449,6 +13772,14 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== +isomorphic-fetch@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -16663,7 +16994,7 @@ node-fetch@2.6.0, node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" -node-fetch@^1.7.3: +node-fetch@^1.0.1, node-fetch@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" dependencies: @@ -16889,6 +17220,11 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.3.0.tgz#9c49e10fc1876aeb76dba88bf1b2b5d9fa57b2ee" integrity sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ== +normalize.css@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" + integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== + normalizeurl@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalizeurl/-/normalizeurl-1.0.0.tgz#4b1a458cd0c7d0856436f69c6b51047ab6855317" @@ -17548,6 +17884,14 @@ p-queue@^6.3.0: eventemitter3 "^4.0.0" p-timeout "^3.1.0" +p-queue@^6.4.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.0.tgz#263f2b73add4cefca81d8d6b2696ee74b326de2f" + integrity sha512-zPHXPNy9jZsiym0PpJjvnHQysx1fSd/QdaNVwiDRLU2KFChD6h9CkCB6b8i3U8lBwJyA+mHgNZCzcy77glUssQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.1.0" + p-reduce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" @@ -17888,6 +18232,11 @@ password-prompt@^1.0.4: ansi-escapes "^3.1.0" cross-spawn "^6.0.5" +patch-console@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/patch-console/-/patch-console-1.0.0.tgz#19b9f028713feb8a3c023702a8cc8cb9f7466f9d" + integrity sha512-nxl9nrnLQmh64iTzMfyylSlRozL7kAXIaxw1fVcLYdyhNkJCRUzirRZTikXGJsg+hc4fqpneTK6iU2H1Q8THSA== + path-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" @@ -18840,6 +19189,13 @@ property-expr@^2.0.2: resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.2.tgz#fff2a43919135553a3bc2fdd94bdb841965b2330" integrity sha512-bc/5ggaYZxNkFKj374aLbEDqVADdYaLcFo8XBkishUWbaAdjlphaBFns9TvRA2pUseVL/wMFmui9X3IdNDU37g== +property-information@5.5.0, property-information@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.5.0.tgz#4dc075d493061a82e2b7d096f406e076ed859943" + integrity sha512-RgEbCx2HLa1chNgvChcx+rrCWD0ctBmGSE0M7lVm1yyv4UbvbrWoXp/BkVLZefzjrRBGW8/Js6uh/BnlHXFyjA== + dependencies: + xtend "^4.0.0" + property-information@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-4.2.0.tgz#f0e66e07cbd6fed31d96844d958d153ad3eb486e" @@ -18853,13 +19209,6 @@ property-information@^5.0.0, property-information@^5.0.1, property-information@^ dependencies: xtend "^4.0.1" -property-information@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.5.0.tgz#4dc075d493061a82e2b7d096f406e076ed859943" - integrity sha512-RgEbCx2HLa1chNgvChcx+rrCWD0ctBmGSE0M7lVm1yyv4UbvbrWoXp/BkVLZefzjrRBGW8/Js6uh/BnlHXFyjA== - dependencies: - xtend "^4.0.0" - proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -19145,6 +19494,11 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-circular-progressbar@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/react-circular-progressbar/-/react-circular-progressbar-2.0.3.tgz#fa8eb59f8db168d2904bae4590641792c80f5991" + integrity sha512-YKN+xAShXA3gYihevbQZbavfiJxo83Dt1cUxqg/cltj4VVsRQpDr7Fg1mvjDG3x1KHGtd9NmYKvJ2mMrPwbKyw== + react-clientside-effect@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.2.tgz#6212fb0e07b204e714581dd51992603d1accc837" @@ -19176,6 +19530,14 @@ react-dev-utils@^4.2.3: strip-ansi "3.0.1" text-table "0.2.0" +react-devtools-core@^4.6.0: + version "4.8.2" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.8.2.tgz#4465f2e8de7795564aa20f28b2f3a9737586db23" + integrity sha512-3Lv3nI8FPAwKqUco35oOlgf+4j8mgYNnIcDv2QTfxEqg2G69q17ZJ8ScU9aBnymS28YC1OW+kTxLmdIQeTN8yg== + dependencies: + shell-quote "^1.6.1" + ws "^7" + react-docgen@^5.0.0: version "5.3.0" resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-5.3.0.tgz#9aabde5e69f1993c8ba839fd9a86696504654589" @@ -19250,7 +19612,7 @@ react-hot-loader@^4.12.21: shallowequal "^1.1.0" source-map "^0.7.3" -react-icons@^3.10.0: +react-icons@^3.0.1, react-icons@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-3.10.0.tgz#6c217a2dde2e8fa8d293210023914b123f317297" integrity sha512-WsQ5n1JToG9VixWilSo1bHv842Cj5aZqTGiS3Ud47myF6aK7S/IUY2+dHcBdmkQcCFRuHsJ9OMUI0kTDfjyZXQ== @@ -19511,6 +19873,15 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +read-pkg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" + integrity sha1-ljYlN48+HE1IyFhytabsfV0JMjc= + dependencies: + normalize-package-data "^2.3.2" + parse-json "^4.0.0" + pify "^3.0.0" + read-pkg@^5.1.1, read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -20435,6 +20806,28 @@ remark-mdx@^1.6.6: remark-parse "8.0.2" unified "9.0.0" +remark-mdx@^2.0.0-next.4, remark-mdx@^2.0.0-next.7: + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-2.0.0-next.7.tgz#e8d4e4fe2c2a98bb34e10304c6e6f2823ba56dfb" + integrity sha512-JHYCfxJzvjTw8h5y10f+mCvbfIt5klAkWlULqPu1nM/r6ghF3tzJl0AFQFj5b/m/7U553+yYb/y4n0julMERYA== + dependencies: + parse-entities "^2.0.0" + remark-stringify "^8.1.0" + stringify-entities "^3.0.1" + strip-indent "^3.0.0" + unist-util-stringify-position "^2.0.3" + +remark-mdxjs@^2.0.0-next.4, remark-mdxjs@^2.0.0-next.7: + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/remark-mdxjs/-/remark-mdxjs-2.0.0-next.7.tgz#32db2b04abb19ee8e7e383103b16f4f555e198dc" + integrity sha512-ixa9jEQ1mB65NYJaBq+Hv91DIqQ7B3wk+L9Agwa31NkIzvt6zcgx6TKwavr0zZG69I2n1gZzekhp51AeVCzU1Q== + dependencies: + "@babel/core" "7.10.5" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.10.4" + "@babel/plugin-syntax-jsx" "7.10.4" + "@mdx-js/util" "^2.0.0-next.7" + remark-message-control@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/remark-message-control/-/remark-message-control-6.0.0.tgz#955b054b38c197c9f2e35b1d88a4912949db7fc5" @@ -21273,6 +21666,16 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rollup-plugin-auto-external@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-auto-external/-/rollup-plugin-auto-external-2.0.0.tgz#98fd137d66c1cbe0f4e245b31560a72dbde896aa" + integrity sha512-HQM3ZkZYfSam1uoZtAB9sK26EiAsfs1phrkf91c/YX+S07wugyRXSigBxrIwiLr5EPPilKYmoMxsrnlGBsXnuQ== + dependencies: + builtins "^2.0.0" + read-pkg "^3.0.0" + safe-resolve "^1.0.0" + semver "^5.5.0" + rollup-plugin-bundle-size@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/rollup-plugin-bundle-size/-/rollup-plugin-bundle-size-1.0.3.tgz#d245cd988486b4040279f9fd33f357f61673e90f" @@ -21292,6 +21695,14 @@ rollup-plugin-hypothetical@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/rollup-plugin-hypothetical/-/rollup-plugin-hypothetical-2.1.0.tgz#7fec9a865ed7d0eac14ca6ee2b2c4088acdb1955" +rollup-plugin-internal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/rollup-plugin-internal/-/rollup-plugin-internal-1.0.4.tgz#3b2f221e4cda5eed51742a0f81617e3840473450" + integrity sha512-abFOORRqps1dICHgLqasbVG+Kt45BDifUj5XuLSGQm/UBnrLuj9ctKxw8388B4YS9Gk6pv3YRjNIsJ/SJKBE9w== + dependencies: + safe-resolve "^1.0.0" + tslib "^1.10.0" + rollup-plugin-postcss@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/rollup-plugin-postcss/-/rollup-plugin-postcss-2.9.0.tgz#e6ea0a1b8fdc4a49fc0385da58804e332750c282" @@ -21377,6 +21788,13 @@ rollup@^2.0.0: optionalDependencies: fsevents "~2.1.2" +rollup@^2.23.0: + version "2.23.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.23.0.tgz#b7ab1fee0c0e60132fd0553c4df1e9cdacfada9d" + integrity sha512-vLNmZFUGVwrnqNAJ/BvuLk1MtWzu4IuoqsH9UWK5AIdO3rt8/CSiJNvPvCIvfzrbNsqKbNzPAG1V2O4eTe2XZg== + optionalDependencies: + fsevents "~2.1.2" + rss@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/rss/-/rss-1.2.2.tgz#50a1698876138133a74f9a05d2bdc8db8d27a921" @@ -21426,6 +21844,13 @@ rxjs@^6.1.0, rxjs@^6.4.0: dependencies: tslib "^1.9.0" +rxjs@^6.5.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== + dependencies: + tslib "^1.9.0" + sade@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/sade/-/sade-1.7.3.tgz#a217ccc4fb4abb2d271648bf48f6628b2636fa1b" @@ -21461,6 +21886,11 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safe-resolve@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-resolve/-/safe-resolve-1.0.0.tgz#fe34f8d29d7a3becfd249d0aa8a799b5c3cf6559" + integrity sha1-/jT40p16O+z9JJ0KqKeZtcPPZVk= + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -22179,6 +22609,11 @@ sparse-bitfield@^3.0.3: dependencies: memory-pager "^1.0.2" +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + spdx-correct@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" @@ -22333,6 +22768,13 @@ stack-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" +stack-utils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" + integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg== + dependencies: + escape-string-regexp "^2.0.0" + stackframe@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.1.tgz#ffef0a3318b1b60c3b58564989aca5660729ec71" @@ -22637,6 +23079,17 @@ stringify-entities@^3.0.0: is-decimal "^1.0.2" is-hexadecimal "^1.0.0" +stringify-entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-3.0.1.tgz#32154b91286ab0869ab2c07696223bd23b6dbfc0" + integrity sha512-Lsk3ISA2++eJYqBMPKcr/8eby1I6L0gP0NlxF8Zja6c05yr/yCYyb2c9PwXjd08Ib3If1vn1rbs1H5ZtVuOfvQ== + dependencies: + character-entities-html4 "^1.0.0" + character-entities-legacy "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.2" + is-hexadecimal "^1.0.0" + stringify-object@^3.2.2, stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" @@ -23187,7 +23640,7 @@ term-size@^2.1.0: resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.1.0.tgz#3aec444c07a7cf936e157c1dc224b590c3c7eef2" integrity sha512-I42EWhJ+2aeNQawGx1VtpO0DFI9YcfuvAMNIdKyf/6sRbHJ4P+ZQ/zIT87tE+ln1ymAGcCJds4dolfSAS0AcNg== -terminal-link@^2.1.1: +terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== @@ -23563,6 +24016,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + trim-lines@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.1.tgz#da738ff58fa74817588455e30b11b85289f2a396" @@ -23697,6 +24155,16 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-fest@0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.15.1.tgz#d2c4e73d3e4a53cf1a906396dd460a1c5178ca00" + integrity sha512-n+UXrN8i5ioo7kqT/nF8xsEzLaqFra7k32SEsSPwvXVGyAcRgV/FUQN/sgfptJTR1oRmmq7z4IXMFSM7im7C9A== + +type-fest@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.12.0.tgz#f57a27ab81c68d136a51fd71467eff94157fa1ee" + integrity sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg== + type-fest@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" @@ -24162,6 +24630,13 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" +unist-util-stringify-position@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + unist-util-visit-children@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/unist-util-visit-children/-/unist-util-visit-children-1.1.2.tgz#bd78b53db9644b9c339ac502854f15471f964f5b" @@ -24181,15 +24656,6 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.3" unist-util-is "^4.0.0" -unist-util-visit@2.0.2, unist-util-visit@^2.0.0, unist-util-visit@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.2.tgz#3843782a517de3d2357b4c193b24af2d9366afb7" - integrity sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" - unist-util-visit@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" @@ -24206,6 +24672,15 @@ unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.3.0, unist dependencies: unist-util-visit-parents "^2.0.0" +unist-util-visit@^2.0.0, unist-util-visit@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.2.tgz#3843782a517de3d2357b4c193b24af2d9366afb7" + integrity sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + universal-url@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universal-url/-/universal-url-2.0.0.tgz#35e7fc2c3374804905cee67ea289ed3a47669809" @@ -24408,6 +24883,14 @@ urltools@^0.4.1: underscore "^1.8.3" urijs "^1.18.2" +urql@^1.9.7: + version "1.10.0" + resolved "https://registry.yarnpkg.com/urql/-/urql-1.10.0.tgz#609f13891d3c5345635764b71b072edd88a1ae7e" + integrity sha512-Cxx1INTWNUMg9A2gyltqNqukOLFVtztkHxFGfv01OvsFAtR+wCmLyJqIzGUZRKVbKepTLocGbelS7QDxtjcqtg== + dependencies: + "@urql/core" "^1.12.3" + wonka "^4.0.14" + urql@^1.9.8: version "1.9.8" resolved "https://registry.yarnpkg.com/urql/-/urql-1.9.8.tgz#07d76efdc698205a55724a2acd2640f7706a036d" @@ -24504,6 +24987,11 @@ uuid@^7.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== +uuid@^8.2.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== + v8-compile-cache@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz#8d32e4f16974654657e676e0e467a348e89b0dc4" @@ -25024,6 +25512,11 @@ whatwg-fetch@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" +whatwg-fetch@>=0.10.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.2.0.tgz#8e134f701f0a4ab5fda82626f113e2b647fd16dc" + integrity sha512-SdGPoQMMnzVYThUbSrEvqTlkvC1Ux27NehaJ/GUHBfNrh5Mjg+1/uRyFMwVnxO2MrikMWvWAqUGgQOfVU4hT7w== + whatwg-fetch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.1.0.tgz#49d630cdfa308dba7f2819d49d09364f540dbcc6" @@ -25401,6 +25894,11 @@ ws@^6.1.0, ws@^6.1.2, ws@^6.2.1: dependencies: async-limiter "~1.0.0" +ws@^7, ws@^7.2.5: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + ws@^7.0.0, ws@^7.1.2, ws@^7.2.3, ws@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" @@ -25512,7 +26010,7 @@ xss@^1.0.6: commander "^2.20.3" cssfilter "0.0.10" -xstate@^4.11.0: +xstate@^4.11.0, xstate@^4.9.1: version "4.11.0" resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.11.0.tgz#dc0bd31079fe22918c2c27c118d6310bef3dcd9e" integrity sha512-v+S3jF2YrM2tFOit8o7+4N3FuFd9IIGcIKHyfHeeNjMlmNmwuiv/IbY9uw7ECifx7H/A9aGLcxPSr0jdjTGDww== @@ -25723,6 +26221,13 @@ yoga-layout-prebuilt@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.9.3.tgz#11e3be29096afe3c284e5d963cc2d628148c1372" +yoga-layout-prebuilt@^1.9.6: + version "1.9.6" + resolved "https://registry.yarnpkg.com/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.9.6.tgz#98dde95bbf8e6e12835876e9305f1e995c4bb801" + integrity sha512-Wursw6uqLXLMjBAO4SEShuzj8+EJXhCF71/rJ7YndHTkRAYSU0GY3OghRqfAk9HPUAAFMuqp3U1Wl+01vmGRQQ== + dependencies: + "@types/yoga-layout" "1.9.2" + yup@^0.27.0: version "0.27.0" resolved "https://registry.yarnpkg.com/yup/-/yup-0.27.0.tgz#f8cb198c8e7dd2124beddc2457571329096b06e7"