next-server
Advanced tools
Comparing version 8.0.0-canary.13 to 8.0.0-canary.14
@@ -7,52 +7,32 @@ "use strict"; | ||
const react_1 = __importDefault(require("react")); | ||
const prop_types_1 = __importDefault(require("prop-types")); | ||
const side_effect_1 = __importDefault(require("./side-effect")); | ||
const head_manager_context_1 = require("./head-manager-context"); | ||
class Head extends react_1.default.Component { | ||
render() { | ||
return null; | ||
} | ||
} | ||
Head.contextType = head_manager_context_1.HeadManagerContext; | ||
const NEXT_HEAD_IDENTIFIER = 'next-head'; | ||
function defaultHead(className = NEXT_HEAD_IDENTIFIER) { | ||
function defaultHead(className = 'next-head') { | ||
return [ | ||
react_1.default.createElement("meta", { key: 'charSet', charSet: 'utf-8', className: className }) | ||
react_1.default.createElement("meta", { key: "charSet", charSet: "utf-8", className: className }), | ||
]; | ||
} | ||
exports.defaultHead = defaultHead; | ||
function reduceComponents(components) { | ||
return components | ||
.map((component) => react_1.default.Children.toArray(component.props.children)) | ||
.reduce((a, b) => a.concat(b), []) | ||
.reduce((a, b) => { | ||
if (react_1.default.Fragment && b.type === react_1.default.Fragment) { | ||
return a.concat(react_1.default.Children.toArray(b.props.children)); | ||
} | ||
return a.concat(b); | ||
}, []) | ||
.reverse() | ||
.concat(defaultHead('')) | ||
.filter(Boolean) | ||
.filter(unique()) | ||
.reverse() | ||
.map((c, i) => { | ||
const className = (c.props && c.props.className ? c.props.className + ' ' : '') + NEXT_HEAD_IDENTIFIER; | ||
const key = c.key || i; | ||
return react_1.default.cloneElement(c, { key, className }); | ||
}); | ||
} | ||
function mapOnServer(head) { | ||
return head; | ||
} | ||
function onStateChange(head) { | ||
if (this.context) { | ||
this.context.updateHead(head); | ||
function onlyReactElement(list, child) { | ||
// React children can be "string" or "number" in this case we ignore them for backwards compat | ||
if (typeof child === "string" || typeof child === "number") { | ||
return list; | ||
} | ||
// Adds support for React.Fragment | ||
if (child.type === react_1.default.Fragment) { | ||
return list.concat(react_1.default.Children.toArray(child.props.children).reduce((fragmentList, fragmentChild) => { | ||
if (typeof fragmentChild === "string" || | ||
typeof fragmentChild === "number") { | ||
return fragmentList; | ||
} | ||
return fragmentList.concat(fragmentChild); | ||
}, [])); | ||
} | ||
return list.concat(child); | ||
} | ||
const METATYPES = ['name', 'httpEquiv', 'charSet', 'itemProp']; | ||
const METATYPES = ["name", "httpEquiv", "charSet", "itemProp"]; | ||
/* | ||
returns a function for filtering head child elements | ||
which shouldn't be duplicated, like <title/>, | ||
except we explicit allow it in ALLOWED_DUPLICATES array | ||
which shouldn't be duplicated, like <title/> | ||
Also adds support for deduplicated `key` properties | ||
*/ | ||
@@ -65,3 +45,3 @@ function unique() { | ||
return (h) => { | ||
if (h.key && h.key.indexOf('.$') === 0) { | ||
if (h.key && typeof h.key !== 'number' && h.key.indexOf(".$") === 0) { | ||
if (keys.has(h.key)) | ||
@@ -73,4 +53,4 @@ return false; | ||
switch (h.type) { | ||
case 'title': | ||
case 'base': | ||
case "title": | ||
case "base": | ||
if (tags.has(h.type)) | ||
@@ -80,3 +60,3 @@ return false; | ||
break; | ||
case 'meta': | ||
case "meta": | ||
for (let i = 0, len = METATYPES.length; i < len; i++) { | ||
@@ -86,3 +66,3 @@ const metatype = METATYPES[i]; | ||
continue; | ||
if (metatype === 'charSet') { | ||
if (metatype === "charSet") { | ||
if (metaTypes.has(metatype)) | ||
@@ -106,8 +86,29 @@ return false; | ||
} | ||
if (process.env.NODE_ENV === 'development') { | ||
const exact = require('prop-types-exact'); | ||
Head.propTypes = exact({ | ||
children: prop_types_1.default.node.isRequired | ||
/** | ||
* | ||
* @param headElement List of multiple <Head> instances | ||
*/ | ||
function reduceComponents(headElements) { | ||
return headElements | ||
.reduce((list, headElement) => { | ||
const headElementChildren = react_1.default.Children.toArray(headElement.props.children); | ||
return list.concat(headElementChildren); | ||
}, []) | ||
.reduce(onlyReactElement, []) | ||
.reverse() | ||
.concat(defaultHead('')) | ||
.filter(unique()) | ||
.reverse() | ||
.map((c, i) => { | ||
const className = (c.props && c.props.className ? c.props.className + " " : "") + | ||
"next-head"; | ||
const key = c.key || i; | ||
return react_1.default.cloneElement(c, { key, className }); | ||
}); | ||
} | ||
exports.default = side_effect_1.default(reduceComponents, onStateChange, mapOnServer)(Head); | ||
const Effect = side_effect_1.default(); | ||
function Head({ children }) { | ||
return (react_1.default.createElement(head_manager_context_1.HeadManagerContext.Consumer, null, (updateHead) => (react_1.default.createElement(Effect, { reduceComponentsToState: reduceComponents, handleStateChange: updateHead }, children)))); | ||
} | ||
Head.rewind = Effect.rewind; | ||
exports.default = Head; |
"use strict"; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const react_1 = __importStar(require("react")); | ||
const utils_1 = require("./utils"); | ||
function withSideEffect(reduceComponentsToState, handleStateChangeOnClient, mapStateOnServer) { | ||
if (typeof reduceComponentsToState !== 'function') { | ||
throw new Error('Expected reduceComponentsToState to be a function.'); | ||
const react_1 = require("react"); | ||
const isServer = typeof window === 'undefined'; | ||
function withSideEffect() { | ||
const mountedInstances = new Set(); | ||
let state; | ||
function emitChange(component) { | ||
state = component.props.reduceComponentsToState([...mountedInstances]); | ||
if (component.props.handleStateChange) { | ||
component.props.handleStateChange(state); | ||
} | ||
} | ||
if (typeof handleStateChangeOnClient !== 'function') { | ||
throw new Error('Expected handleStateChangeOnClient to be a function.'); | ||
} | ||
if (typeof mapStateOnServer !== 'undefined' && typeof mapStateOnServer !== 'function') { | ||
throw new Error('Expected mapStateOnServer to either be undefined or a function.'); | ||
} | ||
return function wrap(WrappedComponent) { | ||
if (typeof WrappedComponent !== 'function') { | ||
throw new Error('Expected WrappedComponent to be a React component.'); | ||
class SideEffect extends react_1.Component { | ||
// Used when server rendering | ||
static rewind() { | ||
const recordedState = state; | ||
state = undefined; | ||
mountedInstances.clear(); | ||
return recordedState; | ||
} | ||
const mountedInstances = new Set(); | ||
let state; | ||
function emitChange(component) { | ||
state = reduceComponentsToState([...mountedInstances]); | ||
if (SideEffect.canUseDOM) { | ||
handleStateChangeOnClient.call(component, state); | ||
} | ||
else if (mapStateOnServer) { | ||
state = mapStateOnServer(state); | ||
} | ||
} | ||
class SideEffect extends react_1.Component { | ||
constructor(props) { | ||
super(props); | ||
if (!SideEffect.canUseDOM) { | ||
mountedInstances.add(this); | ||
emitChange(this); | ||
} | ||
} | ||
static peek() { | ||
return state; | ||
} | ||
static rewind() { | ||
if (SideEffect.canUseDOM) { | ||
throw new Error('You may only call rewind() on the server. Call peek() to read the current state.'); | ||
} | ||
const recordedState = state; | ||
state = undefined; | ||
mountedInstances.clear(); | ||
return recordedState; | ||
} | ||
componentDidMount() { | ||
constructor(props) { | ||
super(props); | ||
if (isServer) { | ||
mountedInstances.add(this); | ||
emitChange(this); | ||
} | ||
componentDidUpdate() { | ||
emitChange(this); | ||
} | ||
componentWillUnmount() { | ||
mountedInstances.delete(this); | ||
emitChange(this); | ||
} | ||
render() { | ||
return react_1.default.createElement(WrappedComponent, null, this.props.children); | ||
} | ||
} | ||
// Expose canUseDOM so tests can monkeypatch it | ||
SideEffect.canUseDOM = typeof window !== 'undefined'; | ||
SideEffect.contextType = WrappedComponent.contextType; | ||
// Try to use displayName of wrapped component | ||
SideEffect.displayName = `SideEffect(${utils_1.getDisplayName(WrappedComponent)})`; | ||
return SideEffect; | ||
}; | ||
componentDidMount() { | ||
mountedInstances.add(this); | ||
emitChange(this); | ||
} | ||
componentDidUpdate() { | ||
emitChange(this); | ||
} | ||
componentWillUnmount() { | ||
mountedInstances.delete(this); | ||
emitChange(this); | ||
} | ||
render() { | ||
return null; | ||
} | ||
} | ||
return SideEffect; | ||
} | ||
exports.default = withSideEffect; |
@@ -189,3 +189,3 @@ "use strict"; | ||
if (this.nextConfig.poweredByHeader) { | ||
res.setHeader('X-Powered-By', 'Next.js ' + "8.0.0-canary.13"); | ||
res.setHeader('X-Powered-By', 'Next.js ' + "8.0.0-canary.14"); | ||
} | ||
@@ -192,0 +192,0 @@ return this.sendHTML(req, res, html); |
{ | ||
"name": "next-server", | ||
"version": "8.0.0-canary.13", | ||
"version": "8.0.0-canary.14", | ||
"main": "./index.js", | ||
@@ -54,3 +54,3 @@ "license": "MIT", | ||
}, | ||
"gitHead": "97bf2679aa94e95cb188b19b465b35e5bf0dfd7d" | ||
"gitHead": "024323b6d85919bb86760e3704434d551fa9a0be" | ||
} |
14
73141
1798