Skip to content

Commit

Permalink
Implement step one of refactoring how we handle internal errors (#2951)
Browse files Browse the repository at this point in the history
* Implement step one of refactoring how we handle internal errors

Borrowed a bunch of code from create-react-app.

Eventual goal is that we centrally manage all errors so we can print
them more tidily in the terminal but also send them to the browser +
other places in the future.

* fix lint + prettier
  • Loading branch information
KyleAMathews committed Nov 17, 2017
1 parent 74b1f3d commit 6ddcc6d
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 40 deletions.
2 changes: 1 addition & 1 deletion examples/using-remark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"gatsby-plugin-catch-links": "latest",
"gatsby-plugin-glamor": "latest",
"gatsby-plugin-sharp": "latest",
"gatsby-remark-autolink-headers": "latest",
"gatsby-remark-autolink-headers": "^1.4.8",
"gatsby-remark-copy-linked-files": "latest",
"gatsby-remark-images": "latest",
"gatsby-remark-katex": "latest",
Expand Down
7 changes: 5 additions & 2 deletions packages/gatsby-cli/src/create-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const path = require(`path`)
const resolveCwd = require(`resolve-cwd`)
const yargs = require(`yargs`)
const report = require(`./reporter`)
const fs = require(`fs`)

const DEFAULT_BROWSERS = [`> 1%`, `last 2 versions`, `IE >= 9`]

Expand All @@ -13,13 +14,15 @@ const handlerP = fn => (...args) => {
}

function buildLocalCommands(cli, isLocalSite) {
const defaultHost = `localhost`
const defaultHost = `0.0.0.0`
const directory = path.resolve(`.`)

let siteInfo = { directory, browserslist: DEFAULT_BROWSERS }
const useYarn = fs.existsSync(path.join(directory, `yarn.lock`))
if (isLocalSite) {
const json = require(path.join(directory, `package.json`))
siteInfo.sitePackageJson = json
siteInfo.appName = json.name
siteInfo.browserslist = json.browserslist || siteInfo.browserslist
}

Expand Down Expand Up @@ -70,7 +73,7 @@ function buildLocalCommands(cli, isLocalSite) {
report.verbose(`set gatsby_executing_command: "${command}"`)

let localCmd = resolveLocalCommand(command)
let args = { ...argv, ...siteInfo }
let args = { ...argv, ...siteInfo, useYarn }

report.verbose(`running command: ${command}`)
return handler ? handler(args, localCmd) : localCmd(args)
Expand Down
4 changes: 3 additions & 1 deletion packages/gatsby-react-router-scroll/src/StateStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export default class SessionStorage {
getStateKey(location, key) {
const locationKey = location.key
const stateKeyBase = `${STATE_KEY_PREFIX}${locationKey}`
return (key === null || typeof key === `undefined`) ? stateKeyBase : `${stateKeyBase}|${key}`
return key === null || typeof key === `undefined`
? stateKeyBase
: `${stateKeyBase}|${key}`
}
}
203 changes: 179 additions & 24 deletions packages/gatsby/src/commands/develop.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* @flow */

const url = require(`url`)
const chokidar = require(`chokidar`)
const express = require(`express`)
const graphqlHTTP = require(`express-graphql`)
Expand All @@ -15,6 +16,11 @@ const developHtml = require(`./develop-html`)
const { withBasePath } = require(`../utils/path`)
const report = require(`gatsby-cli/lib/reporter`)
const launchEditor = require(`react-dev-utils/launchEditor`)
const formatWebpackMessages = require(`react-dev-utils/formatWebpackMessages`)
const chalk = require(`chalk`)
const address = require(`address`)

// const isInteractive = process.stdout.isTTY

// Watch the static directory and copy files to public as they're added or
// changed. Wait 10 seconds so copying doesn't interfer with the regular
Expand Down Expand Up @@ -176,14 +182,6 @@ async function startServer(program) {

report.panic(`There was a problem starting the development server`, err)
}

if (program.open) {
const host =
listener.address().address === `127.0.0.1`
? `localhost`
: listener.address().address
require(`opn`)(`http://${host}:${listener.address().port}`)
}
})

// Register watcher that rebuilds index.html every time html.js changes.
Expand All @@ -195,33 +193,190 @@ async function startServer(program) {
await createIndexHtml()
io.to(`clients`).emit(`reload`)
})

return [compiler, listener]
}

module.exports = (program: any) => {
module.exports = async (program: any) => {
const detect = require(`detect-port`)
const port =
typeof program.port === `string` ? parseInt(program.port, 10) : program.port

detect(port, (err, _port) => {
if (err) {
report.panic(err)
}
let compiler
let listener
await new Promise(resolve => {
detect(port, (err, _port) => {
if (err) {
report.panic(err)
}

if (port !== _port) {
// eslint-disable-next-line max-len
const question = `Something is already running at port ${
port
} \nWould you like to run the app at another port instead? [Y/n] `
if (port !== _port) {
// eslint-disable-next-line max-len
const question = `Something is already running at port ${
port
} \nWould you like to run the app at another port instead? [Y/n] `

return rlInterface.question(question, answer => {
if (answer.length === 0 || answer.match(/^yes|y$/i)) {
program.port = _port // eslint-disable-line no-param-reassign
}
rlInterface.question(question, answer => {
if (answer.length === 0 || answer.match(/^yes|y$/i)) {
program.port = _port // eslint-disable-line no-param-reassign
}

return startServer(program)
startServer(program).then(([c, l]) => {
compiler = c
listener = l
resolve()
})
})
}

startServer(program).then(([c, l]) => {
compiler = c
listener = l
resolve()
})

return null
})
})

function prepareUrls(protocol, host, port) {
const formatUrl = hostname =>
url.format({
protocol,
hostname,
port,
pathname: `/`,
})
const prettyPrintUrl = hostname =>
url.format({
protocol,
hostname,
port: chalk.bold(port),
pathname: `/`,
})

const isUnspecifiedHost = host === `0.0.0.0` || host === `::`
let prettyHost, lanUrlForConfig, lanUrlForTerminal
if (isUnspecifiedHost) {
prettyHost = `localhost`
try {
// This can only return an IPv4 address
lanUrlForConfig = address.ip()
if (lanUrlForConfig) {
// Check if the address is a private ip
// https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
if (
/^10[.]|^172[.](1[6-9]|2[0-9]|3[0-1])[.]|^192[.]168[.]/.test(
lanUrlForConfig
)
) {
// Address is private, format it for later use
lanUrlForTerminal = prettyPrintUrl(lanUrlForConfig)
} else {
// Address is not private, so we will discard it
lanUrlForConfig = undefined
}
}
} catch (_e) {
// ignored
}
} else {
prettyHost = host
}
// TODO collect errors (GraphQL + Webpack) in Redux so we
// can clear terminal and print them out on every compile.
// Borrow pretty printing code from webpack plugin.
const localUrlForTerminal = prettyPrintUrl(prettyHost)
const localUrlForBrowser = formatUrl(prettyHost)
return {
lanUrlForConfig,
lanUrlForTerminal,
localUrlForTerminal,
localUrlForBrowser,
}
}

function printInstructions(appName, urls, useYarn) {
console.log()
console.log(`You can now view ${chalk.bold(appName)} in the browser.`)
console.log()

if (urls.lanUrlForTerminal) {
console.log(
` ${chalk.bold(`Local:`)} ${urls.localUrlForTerminal}`
)
console.log(
` ${chalk.bold(`On Your Network:`)} ${urls.lanUrlForTerminal}`
)
} else {
console.log(` ${urls.localUrlForTerminal}`)
}

return startServer(program)
console.log()
console.log(`Note that the development build is not optimized.`)
console.log(
`To create a production build, use ` + `${chalk.cyan(`gatsby build`)}`
)
console.log()
}

const host =
listener.address().address === `127.0.0.1`
? `localhost`
: listener.address().address

let isFirstCompile = true
// "done" event fires when Webpack has finished recompiling the bundle.
// Whether or not you have warnings or errors, you will get this event.
compiler.plugin(`done`, stats => {
// We have switched off the default Webpack output in WebpackDevServer
// options so we are going to "massage" the warnings and errors and present
// them in a readable focused way.
const messages = formatWebpackMessages(stats.toJson({}, true))
const urls = prepareUrls(`http`, host, port)
const isSuccessful = !messages.errors.length && !messages.warnings.length
// if (isSuccessful) {
// console.log(chalk.green(`Compiled successfully!`))
// }
// if (isSuccessful && (isInteractive || isFirstCompile)) {
if (isSuccessful && isFirstCompile) {
printInstructions(program.appName, urls, program.useYarn)
}

if (program.open) {
require(`opn`)(urls.localUrlForBrowser)
}

isFirstCompile = false

// If errors exist, only show errors.
// if (messages.errors.length) {
// // Only keep the first error. Others are often indicative
// // of the same problem, but confuse the reader with noise.
// if (messages.errors.length > 1) {
// messages.errors.length = 1
// }
// console.log(chalk.red("Failed to compile.\n"))
// console.log(messages.errors.join("\n\n"))
// return
// }

// Show warnings if no errors were found.
// if (messages.warnings.length) {
// console.log(chalk.yellow("Compiled with warnings.\n"))
// console.log(messages.warnings.join("\n\n"))

// // Teach some ESLint tricks.
// console.log(
// "\nSearch for the " +
// chalk.underline(chalk.yellow("keywords")) +
// " to learn more about each warning."
// )
// console.log(
// "To ignore, add " +
// chalk.cyan("// eslint-disable-next-line") +
// " to the line before.\n"
// )
// }
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ async function parseToAst(filePath, fileStr) {
`There was a problem parsing "${filePath}"; any GraphQL ` +
`fragments or queries in this file were not processed. \n` +
`This may indicate a syntax error in the code, or it may be a file type ` +
`That Gatsby does not know how to parse.`,
error
`That Gatsby does not know how to parse.`
)
}
}
Expand Down
16 changes: 6 additions & 10 deletions packages/gatsby/src/utils/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,12 @@ module.exports = async (
new webpack.NamedModulesPlugin(),
new FriendlyErrorsWebpackPlugin({
clearConsole: false,
compilationSuccessInfo: {
messages: [
`Your site is running at http://${program.host}:${
program.port
}`,
`Your graphql debugger is running at http://${program.host}:${
program.port
}/___graphql`,
],
},
// compilationSuccessInfo: {
// messages: [
// `You can now view your site in the browser running at http://${program.host}:${program.port}`,
// `Your graphql debugger is running at http://${program.host}:${program.port}/___graphql`,
// ],
// },
}),
]
case `develop-html`:
Expand Down

0 comments on commit 6ddcc6d

Please sign in to comment.