diff --git a/packages/next-server/lib/utils.js b/packages/next-server/lib/utils.js index 293443ba7a8fb..91aaf8451d0c5 100644 --- a/packages/next-server/lib/utils.js +++ b/packages/next-server/lib/utils.js @@ -63,7 +63,7 @@ export function formatWithValidation (url, options) { if (process.env.NODE_ENV === 'development') { if (url !== null && typeof url === 'object') { Object.keys(url).forEach((key) => { - if (!urlObjectKeys.includes(key)) { + if (urlObjectKeys.indexOf(key) === -1) { console.warn(`Unknown key passed via urlObject into url.format: ${key}`) } }) diff --git a/packages/next/client/event-source-polyfill.js b/packages/next/client/event-source-polyfill.js new file mode 100644 index 0000000000000..f9f8b9cb570f5 --- /dev/null +++ b/packages/next/client/event-source-polyfill.js @@ -0,0 +1,846 @@ +/* eslint-disable */ +// Improved version of https://github.com/Yaffle/EventSource/ +// Available under MIT License (MIT) +// Only tries to support IE11 and nothing below +import fetch from 'unfetch' + +var document = window.document +var Response = window.Response +var TextDecoder = window.TextDecoder +var TextEncoder = window.TextEncoder +var AbortController = window.AbortController + +if (AbortController == undefined) { + AbortController = function () { + this.signal = null + this.abort = function () { + } + } +} + +function TextDecoderPolyfill () { + this.bitsNeeded = 0 + this.codePoint = 0 +} + +TextDecoderPolyfill.prototype.decode = function (octets) { + function valid (codePoint, shift, octetsCount) { + if (octetsCount === 1) { + return codePoint >= 0x0080 >> shift && codePoint << shift <= 0x07FF + } + if (octetsCount === 2) { + return codePoint >= 0x0800 >> shift && codePoint << shift <= 0xD7FF || codePoint >= 0xE000 >> shift && codePoint << shift <= 0xFFFF + } + if (octetsCount === 3) { + return codePoint >= 0x010000 >> shift && codePoint << shift <= 0x10FFFF + } + throw new Error() + } + function octetsCount (bitsNeeded, codePoint) { + if (bitsNeeded === 6 * 1) { + return codePoint >> 6 > 15 ? 3 : codePoint > 31 ? 2 : 1 + } + if (bitsNeeded === 6 * 2) { + return codePoint > 15 ? 3 : 2 + } + if (bitsNeeded === 6 * 3) { + return 3 + } + throw new Error() + } + var REPLACER = 0xFFFD + var string = '' + var bitsNeeded = this.bitsNeeded + var codePoint = this.codePoint + for (var i = 0; i < octets.length; i += 1) { + var octet = octets[i] + if (bitsNeeded !== 0) { + if (octet < 128 || octet > 191 || !valid(codePoint << 6 | octet & 63, bitsNeeded - 6, octetsCount(bitsNeeded, codePoint))) { + bitsNeeded = 0 + codePoint = REPLACER + string += String.fromCharCode(codePoint) + } + } + if (bitsNeeded === 0) { + if (octet >= 0 && octet <= 127) { + bitsNeeded = 0 + codePoint = octet + } else if (octet >= 192 && octet <= 223) { + bitsNeeded = 6 * 1 + codePoint = octet & 31 + } else if (octet >= 224 && octet <= 239) { + bitsNeeded = 6 * 2 + codePoint = octet & 15 + } else if (octet >= 240 && octet <= 247) { + bitsNeeded = 6 * 3 + codePoint = octet & 7 + } else { + bitsNeeded = 0 + codePoint = REPLACER + } + if (bitsNeeded !== 0 && !valid(codePoint, bitsNeeded, octetsCount(bitsNeeded, codePoint))) { + bitsNeeded = 0 + codePoint = REPLACER + } + } else { + bitsNeeded -= 6 + codePoint = codePoint << 6 | octet & 63 + } + if (bitsNeeded === 0) { + if (codePoint <= 0xFFFF) { + string += String.fromCharCode(codePoint) + } else { + string += String.fromCharCode(0xD800 + (codePoint - 0xFFFF - 1 >> 10)) + string += String.fromCharCode(0xDC00 + (codePoint - 0xFFFF - 1 & 0x3FF)) + } + } + } + this.bitsNeeded = bitsNeeded + this.codePoint = codePoint + return string +} + +// Firefox < 38 throws an error with stream option +var supportsStreamOption = function () { + try { + return new TextDecoder().decode(new TextEncoder().encode('test'), { stream: true }) === 'test' + } catch (error) { + console.log(error) + } + return false +} + +// IE, Edge +if (TextDecoder == undefined || TextEncoder == undefined || !supportsStreamOption()) { + TextDecoder = TextDecoderPolyfill +} + +var k = function () { +} + +function XHRWrapper (xhr) { + this.withCredentials = false + this.responseType = '' + this.readyState = 0 + this.status = 0 + this.statusText = '' + this.responseText = '' + this.onprogress = k + this.onreadystatechange = k + this._contentType = '' + this._xhr = xhr + this._sendTimeout = 0 + this._abort = k +} + +XHRWrapper.prototype.open = function (method, url) { + this._abort(true) + + var that = this + var xhr = this._xhr + var state = 1 + var timeout = 0 + + this._abort = function (silent) { + if (that._sendTimeout !== 0) { + clearTimeout(that._sendTimeout) + that._sendTimeout = 0 + } + if (state === 1 || state === 2 || state === 3) { + state = 4 + xhr.onload = k + xhr.onerror = k + xhr.onabort = k + xhr.onprogress = k + xhr.onreadystatechange = k + // IE 8 - 9: XDomainRequest#abort() does not fire any event + // Opera < 10: XMLHttpRequest#abort() does not fire any event + xhr.abort() + if (timeout !== 0) { + clearTimeout(timeout) + timeout = 0 + } + if (!silent) { + that.readyState = 4 + that.onreadystatechange() + } + } + state = 0 + } + + var onStart = function () { + if (state === 1) { + // state = 2; + var status = 0 + var statusText = '' + var contentType = undefined + if (!('contentType' in xhr)) { + try { + status = xhr.status + statusText = xhr.statusText + contentType = xhr.getResponseHeader('Content-Type') + } catch (error) { + // IE < 10 throws exception for `xhr.status` when xhr.readyState === 2 || xhr.readyState === 3 + // Opera < 11 throws exception for `xhr.status` when xhr.readyState === 2 + // https://bugs.webkit.org/show_bug.cgi?id=29121 + status = 0 + statusText = '' + contentType = undefined + // Firefox < 14, Chrome ?, Safari ? + // https://bugs.webkit.org/show_bug.cgi?id=29658 + // https://bugs.webkit.org/show_bug.cgi?id=77854 + } + } else { + status = 200 + statusText = 'OK' + contentType = xhr.contentType + } + if (status !== 0) { + state = 2 + that.readyState = 2 + that.status = status + that.statusText = statusText + that._contentType = contentType + that.onreadystatechange() + } + } + } + var onProgress = function () { + onStart() + if (state === 2 || state === 3) { + state = 3 + var responseText = '' + try { + responseText = xhr.responseText + } catch (error) { + // IE 8 - 9 with XMLHttpRequest + } + that.readyState = 3 + that.responseText = responseText + that.onprogress() + } + } + var onFinish = function () { + // Firefox 52 fires "readystatechange" (xhr.readyState === 4) without final "readystatechange" (xhr.readyState === 3) + // IE 8 fires "onload" without "onprogress" + onProgress() + if (state === 1 || state === 2 || state === 3) { + state = 4 + if (timeout !== 0) { + clearTimeout(timeout) + timeout = 0 + } + that.readyState = 4 + that.onreadystatechange() + } + } + var onReadyStateChange = function () { + if (xhr != undefined) { // Opera 12 + if (xhr.readyState === 4) { + onFinish() + } else if (xhr.readyState === 3) { + onProgress() + } else if (xhr.readyState === 2) { + onStart() + } + } + } + var onTimeout = function () { + timeout = setTimeout(function () { + onTimeout() + }, 500) + if (xhr.readyState === 3) { + onProgress() + } + } + + // XDomainRequest#abort removes onprogress, onerror, onload + xhr.onload = onFinish + xhr.onerror = onFinish + // improper fix to match Firefox behaviour, but it is better than just ignore abort + // see https://bugzilla.mozilla.org/show_bug.cgi?id=768596 + // https://bugzilla.mozilla.org/show_bug.cgi?id=880200 + // https://code.google.com/p/chromium/issues/detail?id=153570 + // IE 8 fires "onload" without "onprogress + xhr.onabort = onFinish + + // https://bugzilla.mozilla.org/show_bug.cgi?id=736723 + if (!('sendAsBinary' in XMLHttpRequest.prototype) && !('mozAnon' in XMLHttpRequest.prototype)) { + xhr.onprogress = onProgress + } + + // IE 8 - 9 (XMLHTTPRequest) + // Opera < 12 + // Firefox < 3.5 + // Firefox 3.5 - 3.6 - ? < 9.0 + // onprogress is not fired sometimes or delayed + // see also #64 + xhr.onreadystatechange = onReadyStateChange + + if ('contentType' in xhr) { + url += (url.indexOf('?') === -1 ? '?' : '&') + 'padding=true' + } + xhr.open(method, url, true) + + if ('readyState' in xhr) { + // workaround for Opera 12 issue with "progress" events + // #91 + timeout = setTimeout(function () { + onTimeout() + }, 0) + } +} +XHRWrapper.prototype.abort = function () { + this._abort(false) +} +XHRWrapper.prototype.getResponseHeader = function (name) { + return this._contentType +} +XHRWrapper.prototype.setRequestHeader = function (name, value) { + var xhr = this._xhr + if ('setRequestHeader' in xhr) { + xhr.setRequestHeader(name, value) + } +} +XHRWrapper.prototype.getAllResponseHeaders = function () { + return this._xhr.getAllResponseHeaders != undefined ? this._xhr.getAllResponseHeaders() : '' +} +XHRWrapper.prototype.send = function () { + // loading indicator in Safari < ? (6), Chrome < 14, Firefox + if (!('ontimeout' in XMLHttpRequest.prototype) && + document != undefined && + document.readyState != undefined && + document.readyState !== 'complete') { + var that = this + that._sendTimeout = setTimeout(function () { + that._sendTimeout = 0 + that.send() + }, 4) + return + } + + var xhr = this._xhr + // withCredentials should be set after "open" for Safari and Chrome (< 19 ?) + xhr.withCredentials = this.withCredentials + xhr.responseType = this.responseType + try { + // xhr.send(); throws "Not enough arguments" in Firefox 3.0 + xhr.send(undefined) + } catch (error1) { + // Safari 5.1.7, Opera 12 + throw error1 + } +} + +function toLowerCase (name) { + return name.replace(/[A-Z]/g, function (c) { + return String.fromCharCode(c.charCodeAt(0) + 0x20) + }) +} + +function HeadersPolyfill (all) { + // Get headers: implemented according to mozilla's example code: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#Example + var map = Object.create(null) + var array = all.split('\r\n') + for (var i = 0; i < array.length; i += 1) { + var line = array[i] + var parts = line.split(': ') + var name = parts.shift() + var value = parts.join(': ') + map[toLowerCase(name)] = value + } + this._map = map +} +HeadersPolyfill.prototype.get = function (name) { + return this._map[toLowerCase(name)] +} + +function XHRTransport () { +} + +XHRTransport.prototype.open = function (xhr, onStartCallback, onProgressCallback, onFinishCallback, url, withCredentials, headers) { + xhr.open('GET', url) + var offset = 0 + xhr.onprogress = function () { + var responseText = xhr.responseText + var chunk = responseText.slice(offset) + offset += chunk.length + onProgressCallback(chunk) + } + xhr.onreadystatechange = function () { + if (xhr.readyState === 2) { + var status = xhr.status + var statusText = xhr.statusText + var contentType = xhr.getResponseHeader('Content-Type') + var headers = xhr.getAllResponseHeaders() + onStartCallback(status, statusText, contentType, new HeadersPolyfill(headers), function () { + xhr.abort() + }) + } else if (xhr.readyState === 4) { + onFinishCallback() + } + } + xhr.withCredentials = withCredentials + xhr.responseType = 'text' + for (var name in headers) { + if (Object.prototype.hasOwnProperty.call(headers, name)) { + xhr.setRequestHeader(name, headers[name]) + } + } + xhr.send() +} + +function HeadersWrapper (headers) { + this._headers = headers +} +HeadersWrapper.prototype.get = function (name) { + return this._headers.get(name) +} + +function FetchTransport () { +} + +FetchTransport.prototype.open = function (xhr, onStartCallback, onProgressCallback, onFinishCallback, url, withCredentials, headers) { + var controller = new AbortController() + var signal = controller.signal// see #120 + var textDecoder = new TextDecoder() + fetch(url, { + headers: headers, + credentials: withCredentials ? 'include' : 'same-origin', + signal: signal, + cache: 'no-store' + }).then(function (response) { + var reader = response.body.getReader() + onStartCallback(response.status, response.statusText, response.headers.get('Content-Type'), new HeadersWrapper(response.headers), function () { + controller.abort() + reader.cancel() + }) + return new Promise(function (resolve, reject) { + var readNextChunk = function () { + reader.read().then(function (result) { + if (result.done) { + // Note: bytes in textDecoder are ignored + resolve(undefined) + } else { + var chunk = textDecoder.decode(result.value, { stream: true }) + onProgressCallback(chunk) + readNextChunk() + } + })['catch'](function (error) { + reject(error) + }) + } + readNextChunk() + }) + })['finally'](function () { + onFinishCallback() + }) +} + +function EventTarget () { + this._listeners = Object.create(null) +} + +function throwError (e) { + setTimeout(function () { + throw e + }, 0) +} + +EventTarget.prototype.dispatchEvent = function (event) { + event.target = this + var typeListeners = this._listeners[event.type] + if (typeListeners != undefined) { + var length = typeListeners.length + for (var i = 0; i < length; i += 1) { + var listener = typeListeners[i] + try { + if (typeof listener.handleEvent === 'function') { + listener.handleEvent(event) + } else { + listener.call(this, event) + } + } catch (e) { + throwError(e) + } + } + } +} +EventTarget.prototype.addEventListener = function (type, listener) { + type = String(type) + var listeners = this._listeners + var typeListeners = listeners[type] + if (typeListeners == undefined) { + typeListeners = [] + listeners[type] = typeListeners + } + var found = false + for (var i = 0; i < typeListeners.length; i += 1) { + if (typeListeners[i] === listener) { + found = true + } + } + if (!found) { + typeListeners.push(listener) + } +} +EventTarget.prototype.removeEventListener = function (type, listener) { + type = String(type) + var listeners = this._listeners + var typeListeners = listeners[type] + if (typeListeners != undefined) { + var filtered = [] + for (var i = 0; i < typeListeners.length; i += 1) { + if (typeListeners[i] !== listener) { + filtered.push(typeListeners[i]) + } + } + if (filtered.length === 0) { + delete listeners[type] + } else { + listeners[type] = filtered + } + } +} + +function Event (type) { + this.type = type + this.target = undefined +} + +function MessageEvent (type, options) { + Event.call(this, type) + this.data = options.data + this.lastEventId = options.lastEventId +} + +MessageEvent.prototype = Object.create(Event.prototype) + +function ConnectionEvent (type, options) { + Event.call(this, type) + this.status = options.status + this.statusText = options.statusText + this.headers = options.headers +} + +ConnectionEvent.prototype = Object.create(Event.prototype) + +var WAITING = -1 +var CONNECTING = 0 +var OPEN = 1 +var CLOSED = 2 + +var AFTER_CR = -1 +var FIELD_START = 0 +var FIELD = 1 +var VALUE_START = 2 +var VALUE = 3 + +var contentTypeRegExp = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i + +var MINIMUM_DURATION = 1000 +var MAXIMUM_DURATION = 18000000 + +var parseDuration = function (value, def) { + var n = parseInt(value, 10) + if (n !== n) { + n = def + } + return clampDuration(n) +} +var clampDuration = function (n) { + return Math.min(Math.max(n, MINIMUM_DURATION), MAXIMUM_DURATION) +} + +var fire = function (that, f, event) { + try { + if (typeof f === 'function') { + f.call(that, event) + } + } catch (e) { + throwError(e) + } +} + +function EventSourcePolyfill (url, options) { + EventTarget.call(this) + + this.onopen = undefined + this.onmessage = undefined + this.onerror = undefined + + this.url = undefined + this.readyState = undefined + this.withCredentials = undefined + + this._close = undefined + + start(this, url, options) +} + +var isFetchSupported = fetch != undefined && Response != undefined && 'body' in Response.prototype + +function start (es, url, options) { + url = String(url) + var withCredentials = options != undefined && Boolean(options.withCredentials) + + var initialRetry = clampDuration(1000) + var heartbeatTimeout = options != undefined && options.heartbeatTimeout != undefined ? parseDuration(options.heartbeatTimeout, 45000) : clampDuration(45000) + + var lastEventId = '' + var retry = initialRetry + var wasActivity = false + var headers = options != undefined && options.headers != undefined ? JSON.parse(JSON.stringify(options.headers)) : undefined + var CurrentTransport = options != undefined && options.Transport != undefined ? options.Transport : XMLHttpRequest + var xhr = isFetchSupported && !(options != undefined && options.Transport != undefined) ? undefined : new XHRWrapper(new CurrentTransport()) + var transport = xhr == undefined ? new FetchTransport() : new XHRTransport() + var cancelFunction = undefined + var timeout = 0 + var currentState = WAITING + var dataBuffer = '' + var lastEventIdBuffer = '' + var eventTypeBuffer = '' + + var textBuffer = '' + var state = FIELD_START + var fieldStart = 0 + var valueStart = 0 + + var onStart = function (status, statusText, contentType, headers, cancel) { + if (currentState === CONNECTING) { + cancelFunction = cancel + if (status === 200 && contentType != undefined && contentTypeRegExp.test(contentType)) { + currentState = OPEN + wasActivity = true + retry = initialRetry + es.readyState = OPEN + var event = new ConnectionEvent('open', { + status: status, + statusText: statusText, + headers: headers + }) + es.dispatchEvent(event) + fire(es, es.onopen, event) + } else { + var message = '' + if (status !== 200) { + if (statusText) { + statusText = statusText.replace(/\s+/g, ' ') + } + message = "EventSource's response has a status " + status + ' ' + statusText + ' that is not 200. Aborting the connection.' + } else { + message = "EventSource's response has a Content-Type specifying an unsupported type: " + (contentType == undefined ? '-' : contentType.replace(/\s+/g, ' ')) + '. Aborting the connection.' + } + throwError(new Error(message)) + close() + var event = new ConnectionEvent('error', { + status: status, + statusText: statusText, + headers: headers + }) + es.dispatchEvent(event) + fire(es, es.onerror, event) + } + } + } + + var onProgress = function (textChunk) { + if (currentState === OPEN) { + var n = -1 + for (var i = 0; i < textChunk.length; i += 1) { + var c = textChunk.charCodeAt(i) + if (c === '\n'.charCodeAt(0) || c === '\r'.charCodeAt(0)) { + n = i + } + } + var chunk = (n !== -1 ? textBuffer : '') + textChunk.slice(0, n + 1) + textBuffer = (n === -1 ? textBuffer : '') + textChunk.slice(n + 1) + if (chunk !== '') { + wasActivity = true + } + for (var position = 0; position < chunk.length; position += 1) { + var c = chunk.charCodeAt(position) + if (state === AFTER_CR && c === '\n'.charCodeAt(0)) { + state = FIELD_START + } else { + if (state === AFTER_CR) { + state = FIELD_START + } + if (c === '\r'.charCodeAt(0) || c === '\n'.charCodeAt(0)) { + if (state !== FIELD_START) { + if (state === FIELD) { + valueStart = position + 1 + } + var field = chunk.slice(fieldStart, valueStart - 1) + var value = chunk.slice(valueStart + (valueStart < position && chunk.charCodeAt(valueStart) === ' '.charCodeAt(0) ? 1 : 0), position) + if (field === 'data') { + dataBuffer += '\n' + dataBuffer += value + } else if (field === 'id') { + lastEventIdBuffer = value + } else if (field === 'event') { + eventTypeBuffer = value + } else if (field === 'retry') { + initialRetry = parseDuration(value, initialRetry) + retry = initialRetry + } else if (field === 'heartbeatTimeout') { + heartbeatTimeout = parseDuration(value, heartbeatTimeout) + if (timeout !== 0) { + clearTimeout(timeout) + timeout = setTimeout(function () { + onTimeout() + }, heartbeatTimeout) + } + } + } + if (state === FIELD_START) { + if (dataBuffer !== '') { + lastEventId = lastEventIdBuffer + if (eventTypeBuffer === '') { + eventTypeBuffer = 'message' + } + var event = new MessageEvent(eventTypeBuffer, { + data: dataBuffer.slice(1), + lastEventId: lastEventIdBuffer + }) + es.dispatchEvent(event) + if (eventTypeBuffer === 'message') { + fire(es, es.onmessage, event) + } + if (currentState === CLOSED) { + return + } + } + dataBuffer = '' + eventTypeBuffer = '' + } + state = c === '\r'.charCodeAt(0) ? AFTER_CR : FIELD_START + } else { + if (state === FIELD_START) { + fieldStart = position + state = FIELD + } + if (state === FIELD) { + if (c === ':'.charCodeAt(0)) { + valueStart = position + 1 + state = VALUE_START + } + } else if (state === VALUE_START) { + state = VALUE + } + } + } + } + } + } + + var onFinish = function () { + if (currentState === OPEN || currentState === CONNECTING) { + currentState = WAITING + if (timeout !== 0) { + clearTimeout(timeout) + timeout = 0 + } + timeout = setTimeout(function () { + onTimeout() + }, retry) + retry = clampDuration(Math.min(initialRetry * 16, retry * 2)) + + es.readyState = CONNECTING + var event = new Event('error') + es.dispatchEvent(event) + fire(es, es.onerror, event) + } + } + + var close = function () { + currentState = CLOSED + if (cancelFunction != undefined) { + cancelFunction() + cancelFunction = undefined + } + if (timeout !== 0) { + clearTimeout(timeout) + timeout = 0 + } + es.readyState = CLOSED + } + + var onTimeout = function () { + timeout = 0 + + if (currentState !== WAITING) { + if (!wasActivity && cancelFunction != undefined) { + throwError(new Error('No activity within ' + heartbeatTimeout + ' milliseconds. Reconnecting.')) + cancelFunction() + cancelFunction = undefined + } else { + wasActivity = false + timeout = setTimeout(function () { + onTimeout() + }, heartbeatTimeout) + } + return + } + + wasActivity = false + timeout = setTimeout(function () { + onTimeout() + }, heartbeatTimeout) + + currentState = CONNECTING + dataBuffer = '' + eventTypeBuffer = '' + lastEventIdBuffer = lastEventId + textBuffer = '' + fieldStart = 0 + valueStart = 0 + state = FIELD_START + + // https://bugzilla.mozilla.org/show_bug.cgi?id=428916 + // Request header field Last-Event-ID is not allowed by Access-Control-Allow-Headers. + var requestURL = url + if (url.slice(0, 5) !== 'data:' && url.slice(0, 5) !== 'blob:') { + if (lastEventId !== '') { + requestURL += (url.indexOf('?') === -1 ? '?' : '&') + 'lastEventId=' + encodeURIComponent(lastEventId) + } + } + var requestHeaders = {} + requestHeaders['Accept'] = 'text/event-stream' + if (headers != undefined) { + for (var name in headers) { + if (Object.prototype.hasOwnProperty.call(headers, name)) { + requestHeaders[name] = headers[name] + } + } + } + try { + transport.open(xhr, onStart, onProgress, onFinish, requestURL, withCredentials, requestHeaders) + } catch (error) { + close() + throw error + } + } + + es.url = url + es.readyState = CONNECTING + es.withCredentials = withCredentials + es._close = close + + onTimeout() +} + +EventSourcePolyfill.prototype = Object.create(EventTarget.prototype) +EventSourcePolyfill.prototype.CONNECTING = CONNECTING +EventSourcePolyfill.prototype.OPEN = OPEN +EventSourcePolyfill.prototype.CLOSED = CLOSED +EventSourcePolyfill.prototype.close = function () { + this._close() +} + +EventSourcePolyfill.CONNECTING = CONNECTING +EventSourcePolyfill.OPEN = OPEN +EventSourcePolyfill.CLOSED = CLOSED +EventSourcePolyfill.prototype.withCredentials = undefined + +export default EventSourcePolyfill diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index 0a93f006d91e2..235bf04f587f1 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -1,4 +1,5 @@ import initNext, * as next from './' +import EventSourcePolyfill from './event-source-polyfill' import initOnDemandEntries from './on-demand-entries-client' import initWebpackHMR from './webpack-hot-middleware-client' @@ -8,6 +9,11 @@ import initWebpackHMR from './webpack-hot-middleware-client' // The runtimeChunk can't hot reload itself currently to correct it when adding pages using on-demand-entries // REPLACE_NOOP_IMPORT +// Support EventSource on Internet Explorer 11 +if (!window.EventSource) { + window.EventSource = EventSourcePolyfill +} + const { __NEXT_DATA__: { assetPrefix diff --git a/packages/next/client/webpack-hot-middleware-client.js b/packages/next/client/webpack-hot-middleware-client.js index 503cf7be450a2..560ddb9621e48 100644 --- a/packages/next/client/webpack-hot-middleware-client.js +++ b/packages/next/client/webpack-hot-middleware-client.js @@ -1,5 +1,5 @@ -import 'event-source-polyfill' import connect from './dev-error-overlay/hot-dev-client' + export default ({ assetPrefix }) => { const options = { path: `${assetPrefix}/_next/webpack-hmr`