Skip to content

Commit

Permalink
fix(gatsby): Make createPage action errors structured (#15619)
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleAMathews authored and wardpeet committed Jul 12, 2019
1 parent e1eaf9a commit 44654be
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 77 deletions.
71 changes: 71 additions & 0 deletions packages/gatsby-cli/src/structured-errors/error-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,77 @@ const errorMap = {
type: `PLUGIN`,
level: `ERROR`,
},
"11322": {
text: context =>
`${
context.pluginName
} created a page and didn't pass the path to the component.\n\nThe page object passed to createPage:\n${JSON.stringify(
context.pageObject,
null,
4
)}\n\nSee the documentation for the "createPage" action — https://www.gatsbyjs.org/docs/actions/#createPage`,
level: `ERROR`,
},
"11323": {
text: context =>
`${
context.pluginName
} must set the page path when creating a page.\n\nThe page object passed to createPage:\n${JSON.stringify(
context.pageObject,
null,
4
)}\n\nSee the documentation for the "createPage" action — https://www.gatsbyjs.org/docs/actions/#createPage`,
level: `ERROR`,
},
"11324": {
text: context =>
`${
context.message
}\n\nSee the documentation for the "createPage" action — https://www.gatsbyjs.org/docs/actions/#createPage`,
level: `ERROR`,
},
"11325": {
text: context =>
`${
context.pluginName
} created a page with a component that doesn't exist.\n\nThe path to the missing component is "${
context.component
}"\n\nThe page object passed to createPage:\n${JSON.stringify(
context.pageObject,
null,
4
)}\n\nSee the documentation for the "createPage" action — https://www.gatsbyjs.org/docs/actions/#createPage`,
level: `ERROR`,
},
"11326": {
text: context =>
`${
context.pluginName
} must set the absolute path to the page component when create creating a page.\n\nThe (relative) path you used for the component is "${
context.component
}"\n\nYou can convert a relative path to an absolute path by requiring the path module and calling path.resolve() e.g.\n\nconst path = require("path")\npath.resolve("${
context.component
}")\n\nThe page object passed to createPage:\n${JSON.stringify(
context.pageObject,
null,
4
)}\n\nSee the documentation for the "createPage" action — https://www.gatsbyjs.org/docs/actions/#createPage`,
level: `ERROR`,
},
"11327": {
text: context =>
`You have an empty file in the "src/pages" directory at "${
context.relativePath
}". Please remove it or make it a valid component`,
level: `ERROR`,
},
"11328": {
text: context =>
`A page component must export a React component for it to be valid. Please make sure this file exports a React component:\n\n${
context.fileName
}`,
level: `ERROR`,
},
}

module.exports = { errorMap, defaultError: errorMap[``] }
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Add pages Fails if component path is missing 1`] = `"The plugin \\"test\\" must set the absolute path to the page component when create creating a page"`;
exports[`Add pages Fails if component path is missing 1`] = `"A component must be set when creating a page"`;

exports[`Add pages Fails if path is missing 1`] = `"The plugin \\"test\\" must set the page path when creating a page"`;

Expand Down
182 changes: 106 additions & 76 deletions packages/gatsby/src/redux/actions/public.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ actions.createPage = (
plugin?: Plugin,
actionOptions?: ActionOptions
) => {
let noPageOrComponent = false
let name = `The plugin "${plugin.name}"`
if (plugin.name === `default-site-plugin`) {
name = `Your site's "gatsby-node.js"`
Expand All @@ -120,13 +119,17 @@ actions.createPage = (
const message = `${name} must set the page path when creating a page`
// Don't log out when testing
if (process.env.NODE_ENV !== `test`) {
console.log(chalk.bold.red(message))
console.log(``)
console.log(page)
report.panic({
id: `11323`,
context: {
pluginName: name,
pageObject: page,
message,
},
})
} else {
return message
}
noPageOrComponent = true
}

// Validate that the context object doesn't overlap with any core page fields
Expand Down Expand Up @@ -176,7 +179,12 @@ ${reservedFields.map(f => ` * "${f}"`).join(`\n`)}
// version. People in v1 often thought that they needed to also pass
// the path to context for it to be available in GraphQL
} else if (invalidFields.some(f => page.context[f] !== page[f])) {
report.panic(error)
report.panic({
id: `11324`,
context: {
message: error,
},
})
} else {
if (!hasWarnedForPageComponentInvalidContext.has(page.component)) {
report.warn(error)
Expand All @@ -186,83 +194,99 @@ ${reservedFields.map(f => ` * "${f}"`).join(`\n`)}
}
}

// Check if a component is set.
if (!page.component) {
if (process.env.NODE_ENV !== `test`) {
report.panic({
id: `11322`,
context: {
pluginName: name,
pageObject: page,
},
})
} else {
// For test
return `A component must be set when creating a page`
}
}

// Don't check if the component exists during tests as we use a lot of fake
// component paths.
if (process.env.NODE_ENV !== `test`) {
if (!fileExistsSync(page.component)) {
const message = `${name} created a page with a component that doesn't exist. Missing component is ${
page.component
}`
console.log(``)
console.log(chalk.bold.red(message))
console.log(``)
console.log(page)
noPageOrComponent = true
} else if (page.component) {
// check if we've processed this component path
// before, before running the expensive "truePath"
// operation
if (pageComponentCache[page.component]) {
page.component = pageComponentCache[page.component]
} else {
const originalPageComponent = page.component

// normalize component path
page.component = slash(page.component)
// check if path uses correct casing - incorrect casing will
// cause issues in query compiler and inconsistencies when
// developing on Mac or Windows and trying to deploy from
// linux CI/CD pipeline
const trueComponentPath = slash(truePath(page.component))
if (trueComponentPath !== page.component) {
if (!hasWarnedForPageComponentInvalidCasing.has(page.component)) {
const markers = page.component
.split(``)
.map((letter, index) => {
if (letter !== trueComponentPath[index]) {
return `^`
}
return ` `
})
.join(``)

report.warn(
stripIndent`
${name} created a page with a component path that doesn't match the casing of the actual file. This may work locally, but will break on systems which are case-sensitive, e.g. most CI/CD pipelines.
page.component: "${page.component}"
path in filesystem: "${trueComponentPath}"
${markers}
`
)
hasWarnedForPageComponentInvalidCasing.add(page.component)
}

page.component = trueComponentPath
}
pageComponentCache[originalPageComponent] = page.component
}
report.panic({
id: `11325`,
context: {
pluginName: name,
pageObject: page,
component: page.component,
},
})
}
}

if (!page.component || !path.isAbsolute(page.component)) {
const message = `${name} must set the absolute path to the page component when create creating a page`
if (!path.isAbsolute(page.component)) {
// Don't log out when testing
if (process.env.NODE_ENV !== `test`) {
console.log(``)
console.log(chalk.bold.red(message))
console.log(``)
console.log(page)
report.panic({
id: `11326`,
context: {
pluginName: name,
pageObject: page,
component: page.component,
},
})
} else {
const message = `${name} must set the absolute path to the page component when create creating a page`
return message
}
noPageOrComponent = true
}

if (noPageOrComponent) {
report.panic(
`See the documentation for createPage https://www.gatsbyjs.org/docs/actions/#createPage`
)
// check if we've processed this component path
// before, before running the expensive "truePath"
// operation
//
// Skip during testing as the paths don't exist on disk.
if (process.env.NODE_ENV !== `test`) {
if (pageComponentCache[page.component]) {
page.component = pageComponentCache[page.component]
} else {
const originalPageComponent = page.component

// normalize component path
page.component = slash(page.component)
// check if path uses correct casing - incorrect casing will
// cause issues in query compiler and inconsistencies when
// developing on Mac or Windows and trying to deploy from
// linux CI/CD pipeline
const trueComponentPath = slash(truePath(page.component))
if (trueComponentPath !== page.component) {
if (!hasWarnedForPageComponentInvalidCasing.has(page.component)) {
const markers = page.component
.split(``)
.map((letter, index) => {
if (letter !== trueComponentPath[index]) {
return `^`
}
return ` `
})
.join(``)

report.warn(
stripIndent`
${name} created a page with a component path that doesn't match the casing of the actual file. This may work locally, but will break on systems which are case-sensitive, e.g. most CI/CD pipelines.
page.component: "${page.component}"
path in filesystem: "${trueComponentPath}"
${markers}
`
)
hasWarnedForPageComponentInvalidCasing.add(page.component)
}

page.component = trueComponentPath
}
pageComponentCache[originalPageComponent] = page.component
}
}

let internalComponentName
Expand Down Expand Up @@ -325,15 +349,21 @@ ${reservedFields.map(f => ` * "${f}"`).join(`\n`)}
)

if (!notEmpty) {
report.panicOnBuild(
`You have an empty file in the "src/pages" directory at "${relativePath}". Please remove it or make it a valid component`
)
report.panicOnBuild({
id: `11327`,
context: {
relativePath,
},
})
}

if (!includesDefaultExport) {
report.panicOnBuild(
`[${fileName}] The page component must export a React component for it to be valid`
)
report.panicOnBuild({
id: `11328`,
context: {
fileName,
},
})
}
}

Expand Down

0 comments on commit 44654be

Please sign in to comment.