Skip to content

Commit

Permalink
Merge branch 'canary'
Browse files Browse the repository at this point in the history
# Conflicts:
#	package.json
#	yarn.lock
  • Loading branch information
timneutkens committed Apr 29, 2018
2 parents a0019b3 + 9578e9f commit f1b9577
Show file tree
Hide file tree
Showing 325 changed files with 6,347 additions and 2,785 deletions.
21 changes: 7 additions & 14 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
{
"presets": [
"env",
"react"
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-flow"
],
"plugins": [
"transform-object-rest-spread",
"transform-class-properties",
"transform-runtime"
],
"env": {
"test": {
"presets": [
"es2015",
"./babel"
]
}
}
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-runtime"
]
}
2 changes: 2 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[ignore]
<PROJECT_ROOT>/examples/.*
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
email: "leo@zeit.co",
tag: "canary",
api_key: {
secure: "mvLXMXn1z1ri29wAy5/CCrDuO7ZC3gxckcnY5W00cbL8aFuK5buvpizKr3dz1pYX7vC55I+nZMtVa2vn4oOlTae07dd0BYGmmpiwq692XD4P+PHVs8D5MlbiT62vIJl/ezv/TcyoAbfIVPfUfLXA7H6a04Kodyzj3LffannOYpZNTPRNSdNAtkVtHPCuHksVpZoj6WhmC52MBEWkh0TCV3OdbeQAs6z3m6j81XcTduhXdraO4UmyXi/ML+eAgEBSejH70/7O/RSGludBOtIWMuxSrEey4wB4F4/xpN7k4LjPQRS+EtUBg9DSb3vB5y/hGwbDqwx7gGfN/yP/ssc9j3G8syTevv3fxH4ver9kg5tvltTJ8/gOzV7nF2sK1+Nb8legL4fLwHvgThf4sB9dAgNc7R8dYNgYwivkLUrrAy9WJsu7OsXLhg3NpEb9PcbKH7yFwrAzQjRPguVxg7bC02g41v0c2i1UXTb0D0/1KW5Mdg7HYZEw4uUXRhJCLWZbOcBCWdmiB+hwKSaq+aO49C5aX2UAyIsUPmG0OgqY91H/Y09KP3O6jN4j7ADrIIrdMwcyx15UJDZMwADCHHesnzeelDah7w3H6q+/heOA4nzbWhZaYQEDK4/aouRDINdXMmCmt9NNYjUA50BAI2dPpqetjaqq3gaPSAXsMvzNvRs="
secure: "rtfolrLprf1or4HkorLic8eVGOvJ6CHla8PNVS0JPVfXIwnqfn9c3OzkKJEcjD+rXLJlmbYfww8mz47cNtjREJVCax6Lc8PEXivhF9rfVCd/zrIslnhxIV6lDSehbm/ZlSItNyGVove+HpgD9G1ky9VGbGoSk0vYHRWriwwfwzfljXgnlJOFzdeJZpbLNEOqbMnjd9/TaaT2CCvInFydp78mx//dG34Qo5Hm6Le6lijEw/Ys6PL0LSJdZ+uP9HUz0of4Pu76IlD+SDPiMdpghidfMbD6s4UJoyeMN4JzX6R8xRHo0Q10yzM1KgbmFvJqXrGZuiVvTv9GviZUIPqowUonqIp6rdAgjLt7B4ng8YoPKCP6xOkjjaGgd6yaNuzbdqiZLRrPYFN6jwD9p4dMirCb3pccEWuDYwkTzJidwH58Isz7B8J2cinMAJbg3DVNlwS5tki9Soydk+n/0d+2Z4euzw+EZ1qd2aFhaLCkDZoLih8AIK6XrvjQTwMJEoRyg6tjhOeXbPNqPpH/k0QvheCeRmwIqXO2Pj8FHY4dgPGgU8PL6wdNJ0WQ+etpWYkuCAGA1SXHcNY2VFhkFTkuC+jFxJWWOfpMlBPdGOYTW31f3OAmp2lMJT2vQkPDqq91RRVorw7Lwf943RSLgULzl4fDuhe9/ORtvHjisqHp+RY="
},
skip_cleanup: true,
on: {
Expand Down
1 change: 1 addition & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/lib/app')
7 changes: 3 additions & 4 deletions bin/next-build
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ if (!existsSync(join(dir, 'pages'))) {
}

build(dir)
.catch((err) => {
console.error(err)
process.exit(1)
})
.catch((err) => {
printAndExit(err)
})
40 changes: 20 additions & 20 deletions bin/next-dev
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,23 @@ if (!existsSync(join(dir, 'pages'))) {

const srv = new Server({ dir, dev: true })
srv.start(argv.port, argv.hostname)
.then(async () => {
if (!process.env.NOW) {
console.log(`> Ready on http://${argv.hostname ? argv.hostname : 'localhost'}:${argv.port}`)
}
})
.catch((err) => {
if (err.code === 'EADDRINUSE') {
let errorMessage = `Port ${argv.port} is already in use.`
const pkgAppPath = require('find-up').sync('package.json', {
cwd: dir
})
const appPackage = JSON.parse(readFileSync(pkgAppPath, 'utf8'))
const nextScript = Object.entries(appPackage.scripts).find(scriptLine => scriptLine[1] === 'next')
if (nextScript) errorMessage += `\nUse \`npm run ${nextScript[0]} -- -p <some other port>\`.`
console.error(errorMessage)
} else {
console.error(err)
}
process.nextTick(() => process.exit(1))
})
.then(async () => {
if (!process.env.NOW) {
console.log(`> Ready on http://${argv.hostname ? argv.hostname : 'localhost'}:${argv.port}`)
}
})
.catch((err) => {
if (err.code === 'EADDRINUSE') {
let errorMessage = `Port ${argv.port} is already in use.`
const pkgAppPath = require('find-up').sync('package.json', {
cwd: dir
})
const appPackage = JSON.parse(readFileSync(pkgAppPath, 'utf8'))
const nextScript = Object.entries(appPackage.scripts).find(scriptLine => scriptLine[1] === 'next')
if (nextScript) errorMessage += `\nUse \`npm run ${nextScript[0]} -- -p <some other port>\`.`
console.error(errorMessage)
} else {
console.error(err)
}
process.nextTick(() => process.exit(1))
})
8 changes: 3 additions & 5 deletions bin/next-export
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ const options = {
outdir: argv.outdir ? resolve(argv.outdir) : resolve(dir, 'out')
}

exportApp(dir, options)
.catch((err) => {
console.error(err)
process.exit(1)
})
exportApp(dir, options).catch((err) => {
printAndExit(err)
})
18 changes: 9 additions & 9 deletions bin/next-start
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ const dir = resolve(argv._[0] || '.')

const srv = new Server({ dir })
srv.start(argv.port, argv.hostname)
.then(() => {
if (!process.env.NOW) {
console.log(`> Ready on http://${argv.hostname ? argv.hostname : 'localhost'}:${argv.port}`)
}
})
.catch((err) => {
console.error(err)
process.exit(1)
})
.then(() => {
if (!process.env.NOW) {
console.log(`> Ready on http://${argv.hostname ? argv.hostname : 'localhost'}:${argv.port}`)
}
})
.catch((err) => {
console.error(err)
process.exit(1)
})
95 changes: 64 additions & 31 deletions client/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { createElement } from 'react'
import React from 'react'
import ReactDOM from 'react-dom'
import HeadManager from './head-manager'
import { createRouter } from '../lib/router'
import EventEmitter from '../lib/EventEmitter'
import App from '../lib/app'
import { loadGetInitialProps, getURL } from '../lib/utils'
import PageLoader from '../lib/page-loader'
import * as asset from '../lib/asset'
Expand Down Expand Up @@ -67,104 +66,138 @@ const errorContainer = document.getElementById('__next-error')
let lastAppProps
export let router
export let ErrorComponent
let HotAppContainer
let ErrorDebugComponent
let Component
let App
let stripAnsi = (s) => s
let applySourcemaps = (e) => e

export const emitter = new EventEmitter()

export default async ({ ErrorDebugComponent: passedDebugComponent, stripAnsi: passedStripAnsi } = {}) => {
export default async ({
HotAppContainer: passedHotAppContainer,
ErrorDebugComponent: passedDebugComponent,
stripAnsi: passedStripAnsi,
applySourcemaps: passedApplySourcemaps
} = {}) => {
// Wait for all the dynamic chunks to get loaded
for (const chunkName of chunks) {
await pageLoader.waitForChunk(chunkName)
}

stripAnsi = passedStripAnsi || stripAnsi
applySourcemaps = passedApplySourcemaps || applySourcemaps
HotAppContainer = passedHotAppContainer
ErrorDebugComponent = passedDebugComponent
ErrorComponent = await pageLoader.loadPage('/_error')
App = await pageLoader.loadPage('/_app')

let initialErr = err

try {
Component = await pageLoader.loadPage(page)
} catch (err) {
console.error(stripAnsi(`${err.message}\n${err.stack}`))
Component = ErrorComponent

if (typeof Component !== 'function') {
throw new Error(`The default export is not a React Component in page: "${pathname}"`)
}
} catch (error) {
// This catches errors like throwing in the top level of a module
initialErr = error
}

router = createRouter(pathname, query, asPath, {
initialProps: props,
pageLoader,
App,
Component,
ErrorComponent,
err
err: initialErr
})

router.subscribe(({ Component, props, hash, err }) => {
render({ Component, props, err, hash, emitter })
})

const hash = location.hash.substring(1)
render({ Component, props, hash, err, emitter })
render({ Component, props, hash, err: initialErr, emitter })

return emitter
}

export async function render (props) {
if (props.err) {
await renderError(props.err)
await renderError(props)
return
}

try {
await doRender(props)
} catch (err) {
if (err.abort) return
await renderError(err)
await renderError({...props, err})
}
}

// This method handles all runtime and debug errors.
// 404 and 500 errors are special kind of errors
// and they are still handle via the main render method.
export async function renderError (error) {
const prod = process.env.NODE_ENV === 'production'
// We need to unmount the current app component because it's
// in the inconsistant state.
// Otherwise, we need to face issues when the issue is fixed and
// it's get notified via HMR
ReactDOM.unmountComponentAtNode(appContainer)

const errorMessage = `${error.message}\n${error.stack}`
console.error(stripAnsi(errorMessage))

if (prod) {
const initProps = { err: error, pathname, query, asPath }
const props = await loadGetInitialProps(ErrorComponent, initProps)
renderReactElement(createElement(ErrorComponent, props), errorContainer)
} else {
renderReactElement(createElement(ErrorDebugComponent, { error }), errorContainer)
export async function renderError (props) {
const {err} = props

// In development we apply sourcemaps to the error
if (process.env.NODE_ENV !== 'production') {
await applySourcemaps(err)
}

const str = stripAnsi(`${err.message}\n${err.stack}${err.info ? `\n\n${err.info.componentStack}` : ''}`)
console.error(str)

if (process.env.NODE_ENV !== 'production') {
// We need to unmount the current app component because it's
// in the inconsistant state.
// Otherwise, we need to face issues when the issue is fixed and
// it's get notified via HMR
ReactDOM.unmountComponentAtNode(appContainer)
renderReactElement(<ErrorDebugComponent error={err} />, errorContainer)
return
}

// In production we do a normal render with the `ErrorComponent` as component.
// `App` will handle the calling of `getInitialProps`, which will include the `err` on the context
await doRender({...props, err, Component: ErrorComponent})
}

async function doRender ({ Component, props, hash, err, emitter: emitterProp = emitter }) {
// Usual getInitialProps fetching is handled in next/router
// this is for when ErrorComponent gets replaced by Component by HMR
if (!props && Component &&
Component !== ErrorComponent &&
lastAppProps.Component === ErrorComponent) {
// fetch props if ErrorComponent was replaced with a page component by HMR
const { pathname, query, asPath } = router
props = await loadGetInitialProps(Component, { err, pathname, query, asPath })
props = await loadGetInitialProps(App, {Component, router, ctx: {err, pathname, query, asPath}})
}

Component = Component || lastAppProps.Component
props = props || lastAppProps.props

const appProps = { Component, props, hash, err, router, headManager }
const appProps = { Component, hash, err, router, headManager, ...props }
// lastAppProps has to be set before ReactDom.render to account for ReactDom throwing an error.
lastAppProps = appProps

emitterProp.emit('before-reactdom-render', { Component, ErrorComponent, appProps })

// We need to clear any existing runtime error messages
ReactDOM.unmountComponentAtNode(errorContainer)
renderReactElement(createElement(App, appProps), appContainer)

// In development we render react-hot-loader's wrapper component
if (HotAppContainer) {
renderReactElement(<HotAppContainer errorReporter={ErrorDebugComponent} warnings={false}>
<App {...appProps} />
</HotAppContainer>, appContainer)
} else {
renderReactElement(<App {...appProps} />, appContainer)
}

emitterProp.emit('after-reactdom-render', { Component, ErrorComponent, appProps })
}
Expand Down
8 changes: 4 additions & 4 deletions client/next-dev.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import stripAnsi from 'strip-ansi'
import initNext, * as next from './'
import ErrorDebugComponent from '../lib/error-debug'
import {ClientDebug} from '../lib/error-debug'
import initOnDemandEntries from './on-demand-entries-client'
import initWebpackHMR from './webpack-hot-middleware-client'

require('@zeit/source-map-support/browser-source-map-support')
import {AppContainer as HotAppContainer} from 'react-hot-loader'
import {applySourcemaps} from './source-map-support'

window.next = next

initNext({ ErrorDebugComponent, stripAnsi })
initNext({ HotAppContainer, ErrorDebugComponent: ClientDebug, applySourcemaps, stripAnsi })
.then((emitter) => {
initOnDemandEntries()
initWebpackHMR()
Expand Down
54 changes: 54 additions & 0 deletions client/source-map-support.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @flow
import fetch from 'unfetch'
const filenameRE = /\(([^)]+\.js):(\d+):(\d+)\)$/

export async function applySourcemaps (e: any): Promise<void> {
if (!e || typeof e.stack !== 'string' || e.sourceMapsApplied) {
return
}

const lines = e.stack.split('\n')

const result = await Promise.all(lines.map((line) => {
return rewriteTraceLine(line)
}))

e.stack = result.join('\n')
// This is to make sure we don't apply the sourcemaps twice on the same object
e.sourceMapsApplied = true
}

async function rewriteTraceLine (trace: string): Promise<string> {
const m = trace.match(filenameRE)
if (m == null) {
return trace
}

const filePath = m[1]
if (filePath.match(/node_modules/)) {
return trace
}

const mapPath = `${filePath}.map`

const res = await fetch(mapPath)
if (res.status !== 200) {
return trace
}

const mapContents = await res.json()
const {SourceMapConsumer} = require('source-map')
const map = new SourceMapConsumer(mapContents)
const originalPosition = map.originalPositionFor({
line: Number(m[2]),
column: Number(m[3])
})

if (originalPosition.source != null) {
const { source, line, column } = originalPosition
const mappedPosition = `(${source.replace(/^webpack:\/\/\//, '')}:${String(line)}:${String(column)})`
return trace.replace(filenameRE, mappedPosition)
}

return trace
}
Loading

0 comments on commit f1b9577

Please sign in to comment.