Created
June 20, 2020 17:31
-
-
Save KyleAMathews/0f1eb3d683ffe28743bb1f9748a2cdd5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/packages/gatsby-recipes/src/gui.js b/packages/gatsby-recipes/src/gui.js | |
index a7baf92b1..378477678 100644 | |
--- a/packages/gatsby-recipes/src/gui.js | |
+++ b/packages/gatsby-recipes/src/gui.js | |
@@ -26,6 +26,7 @@ const { | |
defaultExchanges, | |
subscriptionExchange, | |
} = require(`urql`) | |
+const UrqlProvider = Provider | |
const { SubscriptionClient } = require(`subscriptions-transport-ws`) | |
const slugify = require(`slugify`) | |
require(`normalize.css`) | |
@@ -292,522 +293,523 @@ const removeJsx = () => tree => { | |
return tree | |
} | |
-const RecipeGui = ({ | |
- recipe = `./test.mdx`, | |
- // recipe = `jest.mdx`, | |
- // recipe, | |
- graphqlPort = 4000, | |
- projectRoot = PROJECT_ROOT, | |
-}) => { | |
- try { | |
- const GRAPHQL_ENDPOINT = `http://localhost:${graphqlPort}/graphql` | |
- | |
- const subscriptionClient = new SubscriptionClient( | |
- `ws://localhost:${graphqlPort}/graphql`, | |
- { | |
- reconnect: true, | |
- } | |
- ) | |
+const recipe = `./test.mdx` | |
+// recipe = `jest.mdx`, | |
+// recipe, | |
+const graphqlPort = 4000 | |
+const projectRoot = PROJECT_ROOT | |
- let showRecipesList = false | |
+const GRAPHQL_ENDPOINT = `http://localhost:${graphqlPort}/graphql` | |
- if (!recipe) { | |
- showRecipesList = true | |
- } | |
+const subscriptionClient = new SubscriptionClient( | |
+ `ws://localhost:${graphqlPort}/graphql`, | |
+ { | |
+ reconnect: true, | |
+ } | |
+) | |
+ | |
+let showRecipesList = false | |
- const client = createClient({ | |
- fetch, | |
- url: GRAPHQL_ENDPOINT, | |
- exchanges: [ | |
- ...defaultExchanges, | |
- subscriptionExchange({ | |
- forwardSubscription(operation) { | |
- return subscriptionClient.request(operation) | |
+if (!recipe) { | |
+ showRecipesList = true | |
+} | |
+ | |
+const client = createClient({ | |
+ fetch, | |
+ url: GRAPHQL_ENDPOINT, | |
+ exchanges: [ | |
+ ...defaultExchanges, | |
+ subscriptionExchange({ | |
+ forwardSubscription(operation) { | |
+ return subscriptionClient.request(operation) | |
+ }, | |
+ }), | |
+ ], | |
+}) | |
+ | |
+const ResourcePlan = ({ resourcePlan, isLastPlan }) => ( | |
+ <div id={makeResourceId(resourcePlan)} sx={{}}> | |
+ <div> | |
+ <Styled.p sx={{ mb: resourcePlan.diff ? 6 : 0 }}> | |
+ <Styled.em>{resourcePlan.resourceName}</Styled.em> | |
+ {` `}—{` `} | |
+ {resourcePlan.describe} | |
+ </Styled.p> | |
+ </div> | |
+ {resourcePlan.diff && ( | |
+ <Styled.pre | |
+ sx={{ | |
+ background: theme => theme.tones.BRAND.superLight, | |
+ border: theme => `1px solid ${theme.tones.BRAND.lighter}`, | |
+ padding: 4, | |
+ mb: isLastPlan ? 0 : 6, | |
+ }} | |
+ dangerouslySetInnerHTML={{ | |
+ __html: ansi2HTML(resourcePlan.diff), | |
+ }} | |
+ /> | |
+ )} | |
+ {resourcePlan.resourceChildren | |
+ ? resourcePlan.resourceChildren.map(resource => ( | |
+ <ResourcePlan key={resource._uuid} resourcePlan={resource} /> | |
+ )) | |
+ : null} | |
+ </div> | |
+) | |
+ | |
+const Step = ({ state, step, i }) => { | |
+ const [output, setOutput] = useState({ | |
+ title: ``, | |
+ body: ``, | |
+ date: new Date(), | |
+ }) | |
+ | |
+ const stepResources = state.context?.plan?.filter( | |
+ p => parseInt(p._stepMetadata.step, 10) === i + 1 | |
+ ) | |
+ | |
+ const [complete, setComplete] = useState(false) | |
+ if (output.title !== `` && output.body !== ``) { | |
+ setTimeout(() => { | |
+ setComplete(true) | |
+ }, 0) | |
+ } else { | |
+ setTimeout(() => { | |
+ setComplete(false) | |
+ }, 0) | |
+ } | |
+ | |
+ return ( | |
+ <div | |
+ key={`step-${i}`} | |
+ sx={{ | |
+ border: theme => `1px solid ${theme.tones.BRAND.light}`, | |
+ marginBottom: 7, | |
+ }} | |
+ > | |
+ <div | |
+ sx={{ | |
+ display: `flex`, | |
+ // justifyContent: `space-between`, | |
+ "& > *": { | |
+ marginY: 0, | |
}, | |
- }), | |
- ], | |
- }) | |
+ background: theme => theme.tones.BRAND.lighter, | |
+ padding: 4, | |
+ }} | |
+ > | |
+ <div | |
+ sx={{ | |
+ // marginTop: 2, | |
+ "p:last-child": { | |
+ margin: 0, | |
+ }, | |
+ }} | |
+ > | |
+ <MDX | |
+ key="DOC" | |
+ components={components} | |
+ scope={{ sendEvent }} | |
+ remarkPlugins={[removeJsx]} | |
+ > | |
+ {state.context.exports.join("\n") + "\n\n" + step} | |
+ </MDX> | |
+ </div> | |
+ </div> | |
+ {stepResources?.length > 0 && ( | |
+ <div> | |
+ <div sx={{ padding: 6 }}> | |
+ {stepResources?.map((res, i) => { | |
+ if (res.resourceName === `Input`) { | |
+ if (res.type === `textarea`) { | |
+ return ( | |
+ <div> | |
+ <div> | |
+ <label>{res.label}</label> | |
+ </div> | |
+ <textarea sx={{ border: `1px solid` }} /> | |
+ </div> | |
+ ) | |
+ } | |
+ return ( | |
+ <div> | |
+ <div> | |
+ <label>{res.label}</label> | |
+ </div> | |
+ <input | |
+ sx={{ border: `1px solid` }} | |
+ type={res.type} | |
+ onChange={e => { | |
+ console.log(e.target.value) | |
+ console.log({ res }) | |
+ if (e.target.value !== ``) { | |
+ sendInputEvent({ | |
+ uuid: res._uuid, | |
+ value: e.target.value, | |
+ }) | |
+ } | |
+ }} | |
+ /> | |
+ </div> | |
+ ) | |
+ } | |
+ })} | |
+ </div> | |
+ <div sx={{ padding: 6 }}> | |
+ <Heading | |
+ sx={{ | |
+ marginBottom: 4, | |
+ color: theme => theme.tones.NEUTRAL.darker, | |
+ fontWeight: 500, | |
+ }} | |
+ as={`h3`} | |
+ > | |
+ Proposed changes | |
+ </Heading> | |
+ {stepResources?.map((res, i) => { | |
+ if (res.resourceName !== `Input`) { | |
+ return ( | |
+ <ResourcePlan | |
+ key={`res-plan-${i}`} | |
+ resourcePlan={res} | |
+ isLastPlan={i === stepResources.length - 1} | |
+ /> | |
+ ) | |
+ } | |
+ })} | |
+ </div> | |
+ </div> | |
+ )} | |
+ </div> | |
+ ) | |
+} | |
+ | |
+const sendInputEvent = event => { | |
+ sendEvent({ | |
+ event: `INPUT_ADDED`, | |
+ input: event, | |
+ }) | |
+} | |
- const RecipeInterpreter = () => { | |
- // eslint-disable-next-line | |
- const [localRecipe, setRecipe] = useState(recipe) | |
+const ResourceMessage = ({ state, resource }) => { | |
+ let icon = ( | |
+ <MdBrightness1 | |
+ sx={{ height: `10px`, width: `15px`, display: `inline-block` }} | |
+ /> | |
+ ) | |
+ let message = resource.describe | |
- const [subscriptionResponse] = useSubscription( | |
- { | |
- query: ` | |
+ if (state.value === `applyingPlan` && resource.isDone) { | |
+ icon = <SuccessIcon /> | |
+ } else if (state.value === `applyingPlan`) { | |
+ const keyframe = keyframes` | |
+ 0% { | |
+ transform: rotate(0); | |
+ } | |
+ 100% { | |
+ transform: rotate(360deg); | |
+ } | |
+` | |
+ icon = ( | |
+ <MdRefresh | |
+ sx={{ | |
+ display: `inline-block`, | |
+ animation: `${keyframe} 1s linear infinite`, | |
+ height: `15px`, | |
+ width: `15px`, | |
+ top: `3px`, | |
+ position: `relative`, | |
+ }} | |
+ /> | |
+ ) | |
+ message = resource.describe | |
+ } else if (state.value === `done`) { | |
+ icon = <SuccessIcon height="15px" width="15px" /> | |
+ message = resource._message | |
+ } | |
+ | |
+ return ( | |
+ <> | |
+ {icon} | |
+ {` `} | |
+ <BaseAnchor | |
+ href={`#${makeResourceId(resource)}`} | |
+ onClick={e => { | |
+ 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} | |
+ </BaseAnchor> | |
+ </> | |
+ ) | |
+} | |
+ | |
+const ButtonText = state => { | |
+ if (state.value === `done`) { | |
+ return `Refresh State` | |
+ } | |
+ | |
+ return `Install Recipe` | |
+} | |
+ | |
+const ResourceChildren = ({ resourceChildren }) => { | |
+ if (!resourceChildren || !resourceChildren.length) { | |
+ return null | |
+ } | |
+ | |
+ return ( | |
+ <Styled.ul sx={{ pl: 3, marginTop: 0, mb: 5 }}> | |
+ {resourceChildren.map(resource => ( | |
+ <Styled.li | |
+ sx={{ | |
+ listStyleType: `none`, | |
+ }} | |
+ key={resource._uuid} | |
+ > | |
+ <ResourceMessage state={state} resource={resource} /> | |
+ <ResourceChildren | |
+ state={state} | |
+ resourceChildren={resource.resourceChildren} | |
+ /> | |
+ </Styled.li> | |
+ ))} | |
+ </Styled.ul> | |
+ ) | |
+} | |
+ | |
+function Wrapper({ children }) { | |
+ return <div sx={{ maxWidth: 1000, margin: `0 auto` }}>{children}</div> | |
+} | |
+ | |
+const RecipeInterpreter = () => { | |
+ // eslint-disable-next-line | |
+ const [localRecipe, setRecipe] = useState(recipe) | |
+ | |
+ const [subscriptionResponse] = useSubscription( | |
+ { | |
+ query: ` | |
subscription { | |
operation { | |
state | |
} | |
} | |
`, | |
- }, | |
- (_prev, now) => now | |
- ) | |
+ }, | |
+ (_prev, now) => now | |
+ ) | |
- // eslint-disable-next-line | |
- const [_, createOperation] = useMutation(` | |
+ // eslint-disable-next-line | |
+ const [_, createOperation] = useMutation(` | |
mutation ($recipePath: String!, $projectRoot: String!) { | |
createOperation(recipePath: $recipePath, projectRoot: $projectRoot) | |
} | |
`) | |
- // eslint-disable-next-line | |
- const [__, _sendEvent] = useMutation(` | |
+ // 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 }) | |
- } | |
- } | |
+ 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 }) | |
- } catch (e) { | |
- log(`error creating operation`, e) | |
- } | |
- } | |
+ console.log(`in RecipeInterpreter`, { subscriptionClient }) | |
+ subscriptionClient.connectionCallback = async () => { | |
+ console.log(`connectionCallback`, { | |
+ showRecipesList, | |
+ localRecipe, | |
+ projectRoot, | |
+ }) | |
+ if (!showRecipesList) { | |
+ log(`createOperation`) | |
+ try { | |
+ console.log(`creating operation`) | |
+ await createOperation({ recipePath: localRecipe, projectRoot }) | |
+ } catch (e) { | |
+ log(`error creating operation`, e) | |
} | |
+ } | |
+ } | |
- log(`subscriptionResponse`, subscriptionResponse) | |
- const state = | |
- subscriptionResponse.data && | |
- JSON.parse(subscriptionResponse.data.operation.state) | |
+ log(`subscriptionResponse`, subscriptionResponse) | |
+ const state = | |
+ subscriptionResponse.data && | |
+ JSON.parse(subscriptionResponse.data.operation.state) | |
+ | |
+ log(`subscriptionResponse.data`, subscriptionResponse.data) | |
+ | |
+ if (showRecipesList) { | |
+ return ( | |
+ <Wrapper> | |
+ <WelcomeMessage /> | |
+ <Styled.p>Select a recipe to run</Styled.p> | |
+ <RecipesList | |
+ setRecipe={async recipeItem => { | |
+ showRecipesList = false | |
+ try { | |
+ await createOperation({ | |
+ recipePath: recipeItem, | |
+ projectRoot, | |
+ }) | |
+ } catch (e) { | |
+ log(`error creating operation`, e) | |
+ } | |
+ }} | |
+ /> | |
+ </Wrapper> | |
+ ) | |
+ } | |
+ console.log({ state }) | |
- log(`subscriptionResponse.data`, subscriptionResponse.data) | |
+ if (!state) { | |
+ return ( | |
+ <Wrapper> | |
+ <Spinner /> Loading recipe | |
+ </Wrapper> | |
+ ) | |
+ } | |
- function Wrapper({ children }) { | |
- return <div sx={{ maxWidth: 1000, margin: `0 auto` }}>{children}</div> | |
- } | |
+ const isDone = state.value === `done` | |
- if (showRecipesList) { | |
- return ( | |
- <Wrapper> | |
- <WelcomeMessage /> | |
- <Styled.p>Select a recipe to run</Styled.p> | |
- <RecipesList | |
- setRecipe={async recipeItem => { | |
- showRecipesList = false | |
- try { | |
- await createOperation({ | |
- recipePath: recipeItem, | |
- projectRoot, | |
- }) | |
- } catch (e) { | |
- log(`error creating operation`, e) | |
- } | |
- }} | |
- /> | |
- </Wrapper> | |
- ) | |
- } | |
+ if (state.value === `doneError`) { | |
+ console.log(`doneError state`, state) | |
+ } | |
- if (!state) { | |
- return ( | |
- <Wrapper> | |
- <Spinner /> Loading recipe | |
- </Wrapper> | |
- ) | |
- } | |
+ log(`state`, state) | |
+ log(`plan`, state.context.plan) | |
+ log(`stepResources`, state.context.stepResources) | |
- console.log(state) | |
+ 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, | |
+ }) | |
+ }) | |
- const isDone = state.value === `done` | |
+ log(`staticMessages`, staticMessages) | |
- if (state.value === `doneError`) { | |
- console.log(`doneError state`, state) | |
- } | |
+ if (isDone) { | |
+ process.nextTick(() => { | |
+ subscriptionClient.close() | |
+ log(`The recipe finished successfully`) | |
+ lodash.flattenDeep(state.context.stepResources).forEach((res, i) => { | |
+ log(`✅ ${res._message}\n`) | |
+ }) | |
+ }) | |
+ } | |
- log(`state`, state) | |
- log(`plan`, state.context.plan) | |
- log(`stepResources`, state.context.stepResources) | |
- | |
- const ResourcePlan = ({ resourcePlan, isLastPlan }) => ( | |
- <div id={makeResourceId(resourcePlan)} sx={{}}> | |
- <div> | |
- <Styled.p sx={{ mb: resourcePlan.diff ? 6 : 0 }}> | |
- <Styled.em>{resourcePlan.resourceName}</Styled.em> | |
- {` `}—{` `} | |
- {resourcePlan.describe} | |
- </Styled.p> | |
- </div> | |
- {resourcePlan.diff && ( | |
- <Styled.pre | |
- sx={{ | |
- background: theme => theme.tones.BRAND.superLight, | |
- border: theme => `1px solid ${theme.tones.BRAND.lighter}`, | |
- padding: 4, | |
- mb: isLastPlan ? 0 : 6, | |
- }} | |
- dangerouslySetInnerHTML={{ | |
- __html: ansi2HTML(resourcePlan.diff), | |
- }} | |
- /> | |
- )} | |
- {resourcePlan.resourceChildren | |
- ? resourcePlan.resourceChildren.map(resource => ( | |
- <ResourcePlan key={resource._uuid} resourcePlan={resource} /> | |
- )) | |
- : null} | |
- </div> | |
- ) | |
- | |
- const Step = ({ state, step, i }) => { | |
- const [output, setOutput] = useState({ | |
- title: ``, | |
- body: ``, | |
- date: new Date(), | |
- }) | |
- | |
- const stepResources = state.context?.plan?.filter( | |
- p => parseInt(p._stepMetadata.step, 10) === i + 1 | |
- ) | |
- | |
- const [complete, setComplete] = useState(false) | |
- if (output.title !== `` && output.body !== ``) { | |
- setTimeout(() => { | |
- setComplete(true) | |
- }, 0) | |
- } else { | |
- setTimeout(() => { | |
- setComplete(false) | |
- }, 0) | |
- } | |
+ const plansWithoutInputs = state.context.plan?.filter( | |
+ p => p.resourceName !== `Input` | |
+ ) | |
+ | |
+ const groupedPlans = lodash.groupBy(plansWithoutInputs, p => p.resourceName) | |
+ console.log({ groupedPlans }) | |
- return ( | |
+ return ( | |
+ <> | |
+ <Styled.p>{` `}</Styled.p> | |
+ <InputProvider value={state.context.inputs || {}}> | |
+ <Wrapper> | |
+ <WelcomeMessage /> | |
<div | |
- key={`step-${i}`} | |
sx={{ | |
- border: theme => `1px solid ${theme.tones.BRAND.light}`, | |
- marginBottom: 7, | |
+ mb: 8, | |
+ display: `flex`, | |
+ alignItems: `flex-start`, | |
+ justifyContent: `space-between`, | |
}} | |
> | |
- <div | |
- sx={{ | |
- display: `flex`, | |
- // justifyContent: `space-between`, | |
- "& > *": { | |
- marginY: 0, | |
- }, | |
- background: theme => theme.tones.BRAND.lighter, | |
- padding: 4, | |
- }} | |
- > | |
- <div | |
- sx={{ | |
- // marginTop: 2, | |
- "p:last-child": { | |
- margin: 0, | |
- }, | |
- }} | |
+ <div sx={{ "*:last-child": { mb: 0 } }}> | |
+ <MDX | |
+ components={components} | |
+ scope={{ sendEvent }} | |
+ remarkPlugins={[removeJsx]} | |
> | |
- <MDX | |
- key="DOC" | |
- components={components} | |
- scope={{ sendEvent }} | |
- remarkPlugins={[removeJsx]} | |
- > | |
- {state.context.exports.join("\n") + "\n\n" + step} | |
- </MDX> | |
- </div> | |
+ {state.context.exports.join("\n") + | |
+ "\n\n" + | |
+ state.context.steps[0]} | |
+ </MDX> | |
</div> | |
- {stepResources?.length > 0 && ( | |
- <div sx={{ padding: 6 }}> | |
+ <Button | |
+ onClick={() => sendEvent({ event: `CONTINUE` })} | |
+ loading={state.value === `applyingPlan` ? true : false} | |
+ loadingLabel={`Installing`} | |
+ sx={{ width: `140px`, ml: 6 }} | |
+ > | |
+ <ButtonText state={state} /> | |
+ </Button> | |
+ </div> | |
+ <div sx={{ marginBottom: 8 }}> | |
+ <Heading sx={{ marginBottom: 6 }}> | |
+ Changes | |
+ {` `} | |
+ {plansWithoutInputs && | |
+ `(${plansWithoutInputs.filter(p => p.isDone).length}/${ | |
+ plansWithoutInputs?.length | |
+ })`} | |
+ </Heading> | |
+ {Object.entries(groupedPlans).map(([resourceName, plans]) => ( | |
+ <div key={`key-${resourceName}`}> | |
<Heading | |
- sx={{ | |
- marginBottom: 4, | |
- color: theme => theme.tones.NEUTRAL.darker, | |
- fontWeight: 500, | |
- }} | |
- as={`h3`} | |
+ as="h4" | |
+ sx={{ mb: 3, fontWeight: 400, fontStyle: `italic` }} | |
> | |
- Proposed changes | |
+ {resourceName} | |
</Heading> | |
- {stepResources?.map((res, i) => { | |
- if (res.resourceName === `Input`) { | |
- if (res.type === `textarea`) { | |
- return ( | |
- <div> | |
- <div> | |
- <label>{res.label}</label> | |
- </div> | |
- <textarea sx={{ border: `1px solid` }} /> | |
- </div> | |
- ) | |
- } | |
- return ( | |
- <div> | |
- <div> | |
- <label>{res.label}</label> | |
- </div> | |
- <input | |
- sx={{ border: `1px solid` }} | |
- type={res.type} | |
- onChange={e => { | |
- console.log(e.target.value) | |
- console.log({ res }) | |
- if (e.target.value !== ``) { | |
- sendInputEvent({ | |
- uuid: res._uuid, | |
- value: e.target.value, | |
- }) | |
- } | |
- }} | |
+ <Styled.ul sx={{ pl: 3, marginTop: 0, mb: 5 }}> | |
+ {plans | |
+ .filter(p => p.resourceName !== `Input`) | |
+ .map((p, i) => ( | |
+ <Styled.li | |
+ sx={{ | |
+ listStyleType: `none`, | |
+ }} | |
+ key={`${resourceName}-plan-${i}`} | |
+ > | |
+ <ResourceMessage state={state} resource={p} /> | |
+ <ResourceChildren | |
+ state={state} | |
+ resourceChildren={p.resourceChildren} | |
/> | |
- </div> | |
- ) | |
- } | |
- | |
- return ( | |
- <ResourcePlan | |
- key={`res-plan-${i}`} | |
- resourcePlan={res} | |
- isLastPlan={i === stepResources.length - 1} | |
- /> | |
- ) | |
- })} | |
- </div> | |
- )} | |
- </div> | |
- ) | |
- } | |
- | |
- const sendInputEvent = event => { | |
- sendEvent({ | |
- event: `INPUT_ADDED`, | |
- input: event, | |
- }) | |
- } | |
- | |
- 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) | |
- | |
- if (isDone) { | |
- process.nextTick(() => { | |
- subscriptionClient.close() | |
- log(`The recipe finished successfully`) | |
- lodash.flattenDeep(state.context.stepResources).forEach((res, i) => { | |
- log(`✅ ${res._message}\n`) | |
- }) | |
- }) | |
- } | |
- | |
- const groupedPlans = lodash.groupBy( | |
- state.context.plan, | |
- p => p.resourceName | |
- ) | |
- | |
- const ResourceMessage = ({ resource }) => { | |
- let icon = ( | |
- <MdBrightness1 | |
- sx={{ height: `10px`, width: `15px`, display: `inline-block` }} | |
- /> | |
- ) | |
- let message = resource.describe | |
- | |
- if (state.value === `applyingPlan` && resource.isDone) { | |
- icon = <SuccessIcon /> | |
- } else if (state.value === `applyingPlan`) { | |
- const keyframe = keyframes` | |
- 0% { | |
- transform: rotate(0); | |
- } | |
- 100% { | |
- transform: rotate(360deg); | |
- } | |
-` | |
- icon = ( | |
- <MdRefresh | |
- sx={{ | |
- display: `inline-block`, | |
- animation: `${keyframe} 1s linear infinite`, | |
- height: `15px`, | |
- width: `15px`, | |
- top: `3px`, | |
- position: `relative`, | |
- }} | |
- /> | |
- ) | |
- message = resource.describe | |
- } else if (state.value === `done`) { | |
- icon = <SuccessIcon height="15px" width="15px" /> | |
- message = resource._message | |
- } | |
- | |
- return ( | |
- <> | |
- {icon} | |
- {` `} | |
- <BaseAnchor | |
- href={`#${makeResourceId(resource)}`} | |
- onClick={e => { | |
- 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} | |
- </BaseAnchor> | |
- </> | |
- ) | |
- } | |
- | |
- const ButtonText = () => { | |
- if (state.value === `done`) { | |
- return `Refresh State` | |
- } | |
- | |
- return `Install Recipe` | |
- } | |
- | |
- const ResourceChildren = ({ resourceChildren }) => { | |
- if (!resourceChildren || !resourceChildren.length) { | |
- return null | |
- } | |
- | |
- return ( | |
- <Styled.ul sx={{ pl: 3, marginTop: 0, mb: 5 }}> | |
- {resourceChildren.map(resource => ( | |
- <Styled.li | |
- sx={{ | |
- listStyleType: `none`, | |
- }} | |
- key={resource._uuid} | |
- > | |
- <ResourceMessage resource={resource} /> | |
- <ResourceChildren | |
- resourceChildren={resource.resourceChildren} | |
- /> | |
- </Styled.li> | |
- ))} | |
- </Styled.ul> | |
- ) | |
- } | |
- | |
- return ( | |
- <InputProvider value={state.context.inputs || {}}> | |
- <Wrapper> | |
- <WelcomeMessage /> | |
- <div | |
- sx={{ | |
- mb: 8, | |
- display: `flex`, | |
- alignItems: `flex-start`, | |
- justifyContent: `space-between`, | |
- }} | |
- > | |
- <div sx={{ "*:last-child": { mb: 0 } }}> | |
- <MDX | |
- components={components} | |
- scope={{ sendEvent }} | |
- remarkPlugins={[removeJsx]} | |
- > | |
- {state.context.exports.join("\n") + | |
- "\n\n" + | |
- state.context.steps[0]} | |
- </MDX> | |
+ </Styled.li> | |
+ ))} | |
+ </Styled.ul> | |
</div> | |
- <Button | |
- onClick={() => sendEvent({ event: `CONTINUE` })} | |
- loading={state.value === `applyingPlan` ? true : false} | |
- loadingLabel={`Installing`} | |
- sx={{ width: `140px`, ml: 6 }} | |
- > | |
- <ButtonText /> | |
- </Button> | |
- </div> | |
- <div sx={{ marginBottom: 8 }}> | |
- <Heading sx={{ marginBottom: 6 }}> | |
- Changes | |
- {` `} | |
- {state.context.plan && | |
- `(${state.context.plan.filter(p => p.isDone).length}/${ | |
- state.context.plan?.length | |
- })`} | |
- </Heading> | |
- {Object.entries(groupedPlans).map(([resourceName, plans]) => ( | |
- <div key={`key-${resourceName}`}> | |
- <Heading | |
- as="h4" | |
- sx={{ mb: 3, fontWeight: 400, fontStyle: `italic` }} | |
- > | |
- {resourceName} | |
- </Heading> | |
- <Styled.ul sx={{ pl: 3, marginTop: 0, mb: 5 }}> | |
- {plans | |
- .filter(p => p.resourceName !== `Input`) | |
- .map((p, i) => ( | |
- <Styled.li | |
- sx={{ | |
- listStyleType: `none`, | |
- }} | |
- key={`${resourceName}-plan-${i}`} | |
- > | |
- <ResourceMessage resource={p} /> | |
- <ResourceChildren | |
- resourceChildren={p.resourceChildren} | |
- /> | |
- </Styled.li> | |
- ))} | |
- </Styled.ul> | |
- </div> | |
- ))} | |
- </div> | |
- | |
- <Heading sx={{ mb: 6 }}> | |
- Steps ({state.context.steps.length - 1}) | |
- </Heading> | |
- {state.context.steps.slice(1).map((step, i) => ( | |
- <Step state={state} step={step} key={`step-${i}`} i={i} /> | |
))} | |
- </Wrapper> | |
- </InputProvider> | |
- ) | |
- } | |
- | |
- const Wrapper = () => ( | |
- <Provider value={client}> | |
- <Styled.p>{` `}</Styled.p> | |
- <RecipeInterpreter /> | |
- </Provider> | |
- ) | |
- | |
- const Recipe = () => <Wrapper /> | |
+ </div> | |
- return <Recipe /> | |
- } catch (e) { | |
- log(e) | |
- } | |
+ <Heading sx={{ mb: 6 }}> | |
+ Steps ({state.context.steps.length - 1}) | |
+ </Heading> | |
+ {state.context.steps.slice(1).map((step, i) => ( | |
+ <Step state={state} step={step} key={`step-${i}`} i={i} /> | |
+ ))} | |
+ </Wrapper> | |
+ </InputProvider> | |
+ </> | |
+ ) | |
} | |
const WithProviders = ({ children }) => { | |
@@ -878,14 +880,16 @@ const WithProviders = ({ children }) => { | |
}, | |
} | |
return ( | |
- <ThemeUIProvider theme={theme}> | |
- <ThemeProvider theme={theme}>{children}</ThemeProvider> | |
- </ThemeUIProvider> | |
+ <UrqlProvider value={client}> | |
+ <ThemeUIProvider theme={theme}> | |
+ <ThemeProvider theme={theme}>{children}</ThemeProvider> | |
+ </ThemeUIProvider> | |
+ </UrqlProvider> | |
) | |
} | |
export default () => ( | |
<WithProviders> | |
- <RecipeGui /> | |
+ <RecipeInterpreter /> | |
</WithProviders> | |
) | |
diff --git a/packages/gatsby-recipes/src/renderer/babel-plugin-remove-shortcodes.js b/packages/gatsby-recipes/src/renderer/babel-plugin-remove-shortcodes.js | |
index 1c0b55dbe..33272f9c1 100644 | |
--- a/packages/gatsby-recipes/src/renderer/babel-plugin-remove-shortcodes.js | |
+++ b/packages/gatsby-recipes/src/renderer/babel-plugin-remove-shortcodes.js | |
@@ -44,11 +44,11 @@ module.exports = api => { | |
return | |
} | |
- path.traverse({ | |
- ReturnStatement(innerPath) { | |
- path.replaceWith(innerPath.node) | |
- }, | |
- }) | |
+ // path.traverse({ | |
+ // ReturnStatement(innerPath) { | |
+ // path.replaceWith(innerPath.node) | |
+ // }, | |
+ // }) | |
}, | |
// MDXLayout => doc | |
diff --git a/packages/gatsby-recipes/src/renderer/index.js b/packages/gatsby-recipes/src/renderer/index.js | |
index 342ec4121..3013f732c 100644 | |
--- a/packages/gatsby-recipes/src/renderer/index.js | |
+++ b/packages/gatsby-recipes/src/renderer/index.js | |
@@ -1,6 +1,7 @@ | |
const React = require(`react`) | |
const mdx = require(`@mdx-js/mdx`) | |
const { transform } = require(`@babel/standalone`) | |
+const template = require("@babel/template").default | |
const babelPluginTransformReactJsx = require(`@babel/plugin-transform-react-jsx`) | |
const babelPluginRemoveExportKeywords = require(`babel-plugin-remove-export-keywords`) | |
@@ -10,6 +11,8 @@ const { RecipeStep, RecipeIntroduction } = require(`./step-component`) | |
const Input = require(`./input`).default | |
const { useInputByUuid } = require(`./input-provider`) | |
const babelPluginRemoveShortcodes = require(`./babel-plugin-remove-shortcodes`) | |
+const babelPluginCopyKeyProp = require(`./babel-plugin-copy-key-prop`) | |
+const babelPluginMoveExportKeywords = require(`./babel-plugin-move-export-keywords`) | |
const scope = { | |
React, | |
@@ -20,12 +23,15 @@ const scope = { | |
Config: `div`, // Keep this as a noop for now | |
...resourceComponents, | |
mdx: React.createElement, | |
+ MDXContent: React.createElement, | |
} | |
const transformCodeForEval = code => { | |
// Remove the trailing semicolons so we can turn the component | |
// into a return statement. | |
- const newCode = code.replace(/;\n;$/, ``) | |
+ let newCode = code.replace(/;\n;$/, ``) | |
+ | |
+ newCode = newCode + `\n return MDXContent;` | |
return newCode | |
} | |
@@ -38,7 +44,9 @@ const transformJsx = jsx => { | |
allowReturnOutsideFunction: true, | |
}, | |
plugins: [ | |
- babelPluginRemoveExportKeywords, | |
+ babelPluginCopyKeyProp, | |
+ babelPluginMoveExportKeywords, | |
+ // babelPluginRemoveExportKeywords, | |
babelPluginRemoveShortcodes, | |
[babelPluginTransformReactJsx, { useBuiltIns: true }], | |
], | |
@@ -51,10 +59,19 @@ module.exports = (mdxSrc, cb, context, isApply) => { | |
const scopeKeys = Object.keys(scope) | |
const scopeValues = Object.values(scope) | |
- const jsxFromMdx = mdx.sync(mdxSrc, { skipExport: true }) | |
+ console.log({ mdxSrc }) | |
+ let jsxFromMdx = mdx.sync(mdxSrc, { skipExport: true }) | |
+ console.log(jsxFromMdx) | |
const srcCode = transformJsx(jsxFromMdx) | |
+ console.log(`srcCode`) | |
+ console.log(srcCode) | |
+ | |
+ console.log(`transformed srcCode`) | |
+ console.log(transformCodeForEval(srcCode)) | |
+ | |
const component = new Function(...scopeKeys, transformCodeForEval(srcCode)) | |
+ console.log(`component`, component) | |
try { | |
const result = render(component(...scopeValues), cb, context, isApply) | |
diff --git a/packages/gatsby-recipes/src/renderer/input-provider.js b/packages/gatsby-recipes/src/renderer/input-provider.js | |
index 3d916fc5b..01f32fd85 100644 | |
--- a/packages/gatsby-recipes/src/renderer/input-provider.js | |
+++ b/packages/gatsby-recipes/src/renderer/input-provider.js | |
@@ -4,6 +4,7 @@ const InputContext = React.createContext({}) | |
export const useInputByUuid = uuid => { | |
const context = useContext(InputContext) | |
+ console.log(`useInputByUuid`, { context }) | |
return context[uuid] || {} | |
} | |
diff --git a/packages/gatsby-recipes/src/renderer/input.js b/packages/gatsby-recipes/src/renderer/input.js | |
index 559391fcc..3f155d877 100644 | |
--- a/packages/gatsby-recipes/src/renderer/input.js | |
+++ b/packages/gatsby-recipes/src/renderer/input.js | |
@@ -4,6 +4,7 @@ import { useRecipeStep } from "./step-component" | |
function Input(props) { | |
const step = useRecipeStep() | |
+ console.log(`input stuff`, { props }) | |
return JSON.stringify({ | |
...props, | |
describe: props.label, | |
diff --git a/packages/gatsby-recipes/src/renderer/reconciler.js b/packages/gatsby-recipes/src/renderer/reconciler.js | |
index 24eeddf4c..aca62a300 100644 | |
--- a/packages/gatsby-recipes/src/renderer/reconciler.js | |
+++ b/packages/gatsby-recipes/src/renderer/reconciler.js | |
@@ -98,6 +98,7 @@ const reconciler = ReactReconciler({ | |
const RecipesRenderer = { | |
render(whatToRender, currState) { | |
debug(`rendering recipe`) | |
+ console.log({ whatToRender }) | |
let container = reconciler.createContainer(currState, false, false) | |
reconciler.updateContainer(whatToRender, container, null, null) | |
diff --git a/packages/gatsby-recipes/src/renderer/render.js b/packages/gatsby-recipes/src/renderer/render.js | |
index ffef35c89..d40aa7e55 100644 | |
--- a/packages/gatsby-recipes/src/renderer/render.js | |
+++ b/packages/gatsby-recipes/src/renderer/render.js | |
@@ -139,6 +139,7 @@ const handleResource = (resourceName, context, props) => { | |
const render = async (recipe, cb, inputs = {}, isApply) => { | |
const plan = {} | |
+ console.log(`recipe`, recipe) | |
const recipeWithWrapper = ( | |
<Wrapper inputs={inputs} isApply={isApply}> | |
{recipe} | |
@@ -182,6 +183,7 @@ const render = async (recipe, cb, inputs = {}, isApply) => { | |
// Rerender with the resources and resolve the data from the cache | |
const result = RecipesReconciler.render(recipeWithWrapper, plan) | |
+ console.log({ result }) | |
return transformToPlan(result) | |
} catch (e) { | |
throw e | |
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 9f2460896..5dbf4df31 100644 | |
--- a/packages/gatsby-recipes/src/renderer/transform-to-plan-structure.js | |
+++ b/packages/gatsby-recipes/src/renderer/transform-to-plan-structure.js | |
@@ -12,6 +12,16 @@ const extractPlan = node => { | |
const { _type: type, _props: props, ...plan } = text | |
+ console.log({ type, props, plan }) | |
+ | |
+ if (type === `Input`) { | |
+ return { | |
+ resourceName: type, | |
+ resourceDefinitions: props, | |
+ ...plan, | |
+ } | |
+ } | |
+ | |
if (!type || !providedResources[type]) { | |
return null | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment