Skip to content

Commit 2ab1ca3

Browse files
authored
chore(gatsby-recipes): Move recipe resolving to its own step in state machine (#26280)
Before the parser was sort of overloaded because it was also tasked with figuring out how to resolve the recipe. This moves it to a separate step in the state machine which separates concerns and will set us up nicely for when we allow recipes to import other recipes.
1 parent 3b5504f commit 2ab1ca3

File tree

5 files changed

+103
-97
lines changed

5 files changed

+103
-97
lines changed

packages/gatsby-recipes/src/parser/index.js

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ const remarkMdx = require(`remark-mdx`)
33
const remarkMdxjs = require(`remark-mdxjs`)
44
const remarkParse = require(`remark-parse`)
55
const remarkStringify = require(`remark-stringify`)
6-
const fetch = require(`node-fetch`)
7-
const fs = require(`fs-extra`)
8-
const isUrl = require(`is-url`)
9-
const path = require(`path`)
106
const visit = require(`unist-util-visit`)
117
const remove = require(`unist-util-remove`)
128

@@ -133,56 +129,6 @@ const parse = async src => {
133129
}
134130
}
135131

136-
const isRelative = path => {
137-
if (path.slice(0, 1) == `.`) {
138-
return true
139-
}
140-
141-
return false
142-
}
143-
144-
const getSource = async (pathOrUrl, projectRoot) => {
145-
let recipePath
146-
if (isUrl(pathOrUrl)) {
147-
const res = await fetch(pathOrUrl)
148-
const src = await res.text()
149-
return src
150-
}
151-
if (isRelative(pathOrUrl)) {
152-
recipePath = path.join(projectRoot, pathOrUrl)
153-
} else {
154-
const url = `https://unpkg.com/gatsby-recipes/recipes/${pathOrUrl}`
155-
const res = await fetch(url.endsWith(`.mdx`) ? url : url + `.mdx`)
156-
157-
if (res.status !== 200) {
158-
throw new Error(
159-
JSON.stringify({
160-
fetchError: `Could not fetch ${pathOrUrl} from official recipes`,
161-
})
162-
)
163-
}
164-
165-
const src = await res.text()
166-
return src
167-
}
168-
if (recipePath.slice(-4) !== `.mdx`) {
169-
recipePath += `.mdx`
170-
}
171-
172-
const src = await fs.readFile(recipePath, `utf8`)
173-
return src
174-
}
175-
176-
module.exports = async (recipePath, projectRoot) => {
177-
const src = await getSource(recipePath, projectRoot)
178-
try {
179-
const result = await parse(src)
180-
return result
181-
} catch (e) {
182-
console.log(e)
183-
throw e
184-
}
185-
}
186-
132+
module.exports = parse
187133
module.exports.parse = parse
188134
module.exports.u = u

packages/gatsby-recipes/src/parser/index.test.js

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,6 @@ const parser = require(`.`)
66
const fixturePath = path.join(__dirname, `fixtures/prettier-git-hook.mdx`)
77
const fixtureSrc = fs.readFileSync(fixturePath, `utf8`)
88

9-
// There are network calls being made and thoses tests often doesn't finish
10-
// within default 5s timeout on CI
11-
jest.setTimeout(100000)
12-
13-
test(`fetches a recipe from unpkg when official short form`, async () => {
14-
const result = await parser(`theme-ui`)
15-
16-
expect(result.stepsAsMdx[0]).toMatch(`# Setup Theme UI`)
17-
expect(result.stepsAsMdx[1]).toMatch(`Installs packages`)
18-
expect(result.stepsAsMdx[1]).toMatch(`<NPMPackage`)
19-
expect(result.stepsAsMdx[1]).toMatch(`_uuid="`)
20-
})
21-
22-
test(`fetches a recipe from unpkg when official short form and .mdx`, async () => {
23-
const result = await parser(`theme-ui.mdx`)
24-
25-
expect(result).toBeTruthy()
26-
})
27-
28-
test(`raises an error when the recipe isn't known`, async () => {
29-
try {
30-
await parser(`theme-uiz`)
31-
} catch (e) {
32-
expect(e).toBeTruthy()
33-
}
34-
})
35-
369
test(`partitions the MDX into steps`, async () => {
3710
const result = await parser.parse(fixtureSrc)
3811

packages/gatsby-recipes/src/recipe-machine/index.js

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ const createPlan = require(`../create-plan`)
66
const applyPlan = require(`../apply-plan`)
77
const validateSteps = require(`../validate-steps`)
88
const parser = require(`../parser`)
9+
const resolveRecipe = require(`../resolve-recipe`)
910

1011
const recipeMachine = Machine(
1112
{
1213
id: `recipe`,
13-
initial: `parsingRecipe`,
14+
initial: `resolvingRecipe`,
1415
context: {
1516
recipePath: null,
1617
projectRoot: null,
1718
recipe: ``,
19+
recipeSrc: ``,
1820
stepsAsMdx: [],
1921
exports: [],
2022
plan: [],
@@ -23,28 +25,50 @@ const recipeMachine = Machine(
2325
inputs: {},
2426
},
2527
states: {
26-
parsingRecipe: {
28+
resolvingRecipe: {
2729
invoke: {
28-
id: `parseRecipe`,
30+
id: `resolveRecipe`,
2931
src: async (context, _event) => {
30-
debug(`parsingRecipe`)
31-
32-
let result
3332
if (context.src) {
34-
result = await parser.parse(context.src)
33+
return context.src
3534
} else if (context.recipePath && context.projectRoot) {
36-
result = await parser(context.recipePath, context.projectRoot)
37-
} else {
38-
throw new Error(
39-
JSON.stringify({
40-
validationError: `A recipe must be specified`,
41-
})
35+
const recipe = await resolveRecipe(
36+
context.recipePath,
37+
context.projectRoot
4238
)
39+
return recipe
40+
} else {
41+
throw new Error(`A recipe must be specified`)
4342
}
44-
43+
},
44+
onError: {
45+
target: `doneError`,
46+
actions: assign({
47+
error: (context, _event) => {
48+
debug(`error resolving recipe`)
49+
return {
50+
error: `Could not resolve recipe "${context.recipePath}"`,
51+
}
52+
},
53+
}),
54+
},
55+
onDone: {
56+
target: `parsingRecipe`,
57+
actions: assign({
58+
recipeSrc: (_context, event) => event.data,
59+
}),
60+
},
61+
},
62+
},
63+
parsingRecipe: {
64+
invoke: {
65+
id: `parseRecipe`,
66+
src: async (context, _event) => {
67+
debug(`parsingRecipe`)
68+
const parsed = await parser.parse(context.recipeSrc)
4569
debug(`parsedRecipe`)
4670

47-
return result
71+
return parsed
4872
},
4973
onError: {
5074
target: `doneError`,

packages/gatsby-recipes/src/recipe-machine/index.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,23 @@ describe(`recipe-machine`, () => {
7777

7878
service.start()
7979
})
80+
81+
it(`fetches official recipes from unpkg`, done => {
82+
const initialContext = {
83+
recipePath: `theme-ui`,
84+
projectRoot: `/Users/fake`,
85+
currentStep: 0,
86+
}
87+
const service = interpret(
88+
recipeMachine.withContext(initialContext)
89+
).onTransition(state => {
90+
if (state.value === `presentPlan`) {
91+
expect(state.context.plan.length).toBeGreaterThan(1)
92+
service.stop()
93+
done()
94+
}
95+
})
96+
97+
service.start()
98+
})
8099
})
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const fs = require(`fs-extra`)
2+
const path = require(`path`)
3+
const isUrl = require(`is-url`)
4+
const fetch = require(`node-fetch`)
5+
6+
const isRelative = path => {
7+
if (path.slice(0, 1) == `.`) {
8+
return true
9+
}
10+
11+
return false
12+
}
13+
14+
module.exports = async (pathOrUrl, projectRoot) => {
15+
let recipePath
16+
if (isUrl(pathOrUrl)) {
17+
const res = await fetch(pathOrUrl)
18+
const src = await res.text()
19+
return src
20+
}
21+
if (isRelative(pathOrUrl)) {
22+
recipePath = path.join(projectRoot, pathOrUrl)
23+
} else {
24+
const url = `https://unpkg.com/gatsby-recipes/recipes/${pathOrUrl}`
25+
const res = await fetch(url.endsWith(`.mdx`) ? url : url + `.mdx`)
26+
27+
if (res.status !== 200) {
28+
throw new Error(
29+
JSON.stringify({
30+
fetchError: `Could not fetch ${pathOrUrl} from official recipes`,
31+
})
32+
)
33+
}
34+
35+
const src = await res.text()
36+
return src
37+
}
38+
if (recipePath.slice(-4) !== `.mdx`) {
39+
recipePath += `.mdx`
40+
}
41+
42+
const src = await fs.readFile(recipePath, `utf8`)
43+
return src
44+
}

0 commit comments

Comments
 (0)