UNPKG

35.3 kBJavaScriptView Raw
1import PropTypes from 'prop-types';
2import withSideEffect from 'react-side-effect';
3import isEqual from 'react-fast-compare';
4import React from 'react';
5import objectAssign from 'object-assign';
6
7var ATTRIBUTE_NAMES = {
8 BODY: "bodyAttributes",
9 HTML: "htmlAttributes",
10 TITLE: "titleAttributes"
11};
12
13var TAG_NAMES = {
14 BASE: "base",
15 BODY: "body",
16 HEAD: "head",
17 HTML: "html",
18 LINK: "link",
19 META: "meta",
20 NOSCRIPT: "noscript",
21 SCRIPT: "script",
22 STYLE: "style",
23 TITLE: "title"
24};
25
26var VALID_TAG_NAMES = Object.keys(TAG_NAMES).map(function (name) {
27 return TAG_NAMES[name];
28});
29
30var TAG_PROPERTIES = {
31 CHARSET: "charset",
32 CSS_TEXT: "cssText",
33 HREF: "href",
34 HTTPEQUIV: "http-equiv",
35 INNER_HTML: "innerHTML",
36 ITEM_PROP: "itemprop",
37 NAME: "name",
38 PROPERTY: "property",
39 REL: "rel",
40 SRC: "src",
41 TARGET: "target"
42};
43
44var REACT_TAG_MAP = {
45 accesskey: "accessKey",
46 charset: "charSet",
47 class: "className",
48 contenteditable: "contentEditable",
49 contextmenu: "contextMenu",
50 "http-equiv": "httpEquiv",
51 itemprop: "itemProp",
52 tabindex: "tabIndex"
53};
54
55var HELMET_PROPS = {
56 DEFAULT_TITLE: "defaultTitle",
57 DEFER: "defer",
58 ENCODE_SPECIAL_CHARACTERS: "encodeSpecialCharacters",
59 ON_CHANGE_CLIENT_STATE: "onChangeClientState",
60 TITLE_TEMPLATE: "titleTemplate"
61};
62
63var HTML_TAG_MAP = Object.keys(REACT_TAG_MAP).reduce(function (obj, key) {
64 obj[REACT_TAG_MAP[key]] = key;
65 return obj;
66}, {});
67
68var SELF_CLOSING_TAGS = [TAG_NAMES.NOSCRIPT, TAG_NAMES.SCRIPT, TAG_NAMES.STYLE];
69
70var HELMET_ATTRIBUTE = "data-react-helmet";
71
72var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
73 return typeof obj;
74} : function (obj) {
75 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
76};
77
78var classCallCheck = function (instance, Constructor) {
79 if (!(instance instanceof Constructor)) {
80 throw new TypeError("Cannot call a class as a function");
81 }
82};
83
84var createClass = function () {
85 function defineProperties(target, props) {
86 for (var i = 0; i < props.length; i++) {
87 var descriptor = props[i];
88 descriptor.enumerable = descriptor.enumerable || false;
89 descriptor.configurable = true;
90 if ("value" in descriptor) descriptor.writable = true;
91 Object.defineProperty(target, descriptor.key, descriptor);
92 }
93 }
94
95 return function (Constructor, protoProps, staticProps) {
96 if (protoProps) defineProperties(Constructor.prototype, protoProps);
97 if (staticProps) defineProperties(Constructor, staticProps);
98 return Constructor;
99 };
100}();
101
102var _extends = Object.assign || function (target) {
103 for (var i = 1; i < arguments.length; i++) {
104 var source = arguments[i];
105
106 for (var key in source) {
107 if (Object.prototype.hasOwnProperty.call(source, key)) {
108 target[key] = source[key];
109 }
110 }
111 }
112
113 return target;
114};
115
116var inherits = function (subClass, superClass) {
117 if (typeof superClass !== "function" && superClass !== null) {
118 throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
119 }
120
121 subClass.prototype = Object.create(superClass && superClass.prototype, {
122 constructor: {
123 value: subClass,
124 enumerable: false,
125 writable: true,
126 configurable: true
127 }
128 });
129 if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
130};
131
132var objectWithoutProperties = function (obj, keys) {
133 var target = {};
134
135 for (var i in obj) {
136 if (keys.indexOf(i) >= 0) continue;
137 if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
138 target[i] = obj[i];
139 }
140
141 return target;
142};
143
144var possibleConstructorReturn = function (self, call) {
145 if (!self) {
146 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
147 }
148
149 return call && (typeof call === "object" || typeof call === "function") ? call : self;
150};
151
152var encodeSpecialCharacters = function encodeSpecialCharacters(str) {
153 var encode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
154
155 if (encode === false) {
156 return String(str);
157 }
158
159 return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
160};
161
162var getTitleFromPropsList = function getTitleFromPropsList(propsList) {
163 var innermostTitle = getInnermostProperty(propsList, TAG_NAMES.TITLE);
164 var innermostTemplate = getInnermostProperty(propsList, HELMET_PROPS.TITLE_TEMPLATE);
165
166 if (innermostTemplate && innermostTitle) {
167 // use function arg to avoid need to escape $ characters
168 return innermostTemplate.replace(/%s/g, function () {
169 return Array.isArray(innermostTitle) ? innermostTitle.join("") : innermostTitle;
170 });
171 }
172
173 var innermostDefaultTitle = getInnermostProperty(propsList, HELMET_PROPS.DEFAULT_TITLE);
174
175 return innermostTitle || innermostDefaultTitle || undefined;
176};
177
178var getOnChangeClientState = function getOnChangeClientState(propsList) {
179 return getInnermostProperty(propsList, HELMET_PROPS.ON_CHANGE_CLIENT_STATE) || function () {};
180};
181
182var getAttributesFromPropsList = function getAttributesFromPropsList(tagType, propsList) {
183 return propsList.filter(function (props) {
184 return typeof props[tagType] !== "undefined";
185 }).map(function (props) {
186 return props[tagType];
187 }).reduce(function (tagAttrs, current) {
188 return _extends({}, tagAttrs, current);
189 }, {});
190};
191
192var getBaseTagFromPropsList = function getBaseTagFromPropsList(primaryAttributes, propsList) {
193 return propsList.filter(function (props) {
194 return typeof props[TAG_NAMES.BASE] !== "undefined";
195 }).map(function (props) {
196 return props[TAG_NAMES.BASE];
197 }).reverse().reduce(function (innermostBaseTag, tag) {
198 if (!innermostBaseTag.length) {
199 var keys = Object.keys(tag);
200
201 for (var i = 0; i < keys.length; i++) {
202 var attributeKey = keys[i];
203 var lowerCaseAttributeKey = attributeKey.toLowerCase();
204
205 if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && tag[lowerCaseAttributeKey]) {
206 return innermostBaseTag.concat(tag);
207 }
208 }
209 }
210
211 return innermostBaseTag;
212 }, []);
213};
214
215var getTagsFromPropsList = function getTagsFromPropsList(tagName, primaryAttributes, propsList) {
216 // Calculate list of tags, giving priority innermost component (end of the propslist)
217 var approvedSeenTags = {};
218
219 return propsList.filter(function (props) {
220 if (Array.isArray(props[tagName])) {
221 return true;
222 }
223 if (typeof props[tagName] !== "undefined") {
224 warn("Helmet: " + tagName + " should be of type \"Array\". Instead found type \"" + _typeof(props[tagName]) + "\"");
225 }
226 return false;
227 }).map(function (props) {
228 return props[tagName];
229 }).reverse().reduce(function (approvedTags, instanceTags) {
230 var instanceSeenTags = {};
231
232 instanceTags.filter(function (tag) {
233 var primaryAttributeKey = void 0;
234 var keys = Object.keys(tag);
235 for (var i = 0; i < keys.length; i++) {
236 var attributeKey = keys[i];
237 var lowerCaseAttributeKey = attributeKey.toLowerCase();
238
239 // Special rule with link tags, since rel and href are both primary tags, rel takes priority
240 if (primaryAttributes.indexOf(lowerCaseAttributeKey) !== -1 && !(primaryAttributeKey === TAG_PROPERTIES.REL && tag[primaryAttributeKey].toLowerCase() === "canonical") && !(lowerCaseAttributeKey === TAG_PROPERTIES.REL && tag[lowerCaseAttributeKey].toLowerCase() === "stylesheet")) {
241 primaryAttributeKey = lowerCaseAttributeKey;
242 }
243 // Special case for innerHTML which doesn't work lowercased
244 if (primaryAttributes.indexOf(attributeKey) !== -1 && (attributeKey === TAG_PROPERTIES.INNER_HTML || attributeKey === TAG_PROPERTIES.CSS_TEXT || attributeKey === TAG_PROPERTIES.ITEM_PROP)) {
245 primaryAttributeKey = attributeKey;
246 }
247 }
248
249 if (!primaryAttributeKey || !tag[primaryAttributeKey]) {
250 return false;
251 }
252
253 var value = tag[primaryAttributeKey].toLowerCase();
254
255 if (!approvedSeenTags[primaryAttributeKey]) {
256 approvedSeenTags[primaryAttributeKey] = {};
257 }
258
259 if (!instanceSeenTags[primaryAttributeKey]) {
260 instanceSeenTags[primaryAttributeKey] = {};
261 }
262
263 if (!approvedSeenTags[primaryAttributeKey][value]) {
264 instanceSeenTags[primaryAttributeKey][value] = true;
265 return true;
266 }
267
268 return false;
269 }).reverse().forEach(function (tag) {
270 return approvedTags.push(tag);
271 });
272
273 // Update seen tags with tags from this instance
274 var keys = Object.keys(instanceSeenTags);
275 for (var i = 0; i < keys.length; i++) {
276 var attributeKey = keys[i];
277 var tagUnion = objectAssign({}, approvedSeenTags[attributeKey], instanceSeenTags[attributeKey]);
278
279 approvedSeenTags[attributeKey] = tagUnion;
280 }
281
282 return approvedTags;
283 }, []).reverse();
284};
285
286var getInnermostProperty = function getInnermostProperty(propsList, property) {
287 for (var i = propsList.length - 1; i >= 0; i--) {
288 var props = propsList[i];
289
290 if (props.hasOwnProperty(property)) {
291 return props[property];
292 }
293 }
294
295 return null;
296};
297
298var reducePropsToState = function reducePropsToState(propsList) {
299 return {
300 baseTag: getBaseTagFromPropsList([TAG_PROPERTIES.HREF, TAG_PROPERTIES.TARGET], propsList),
301 bodyAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.BODY, propsList),
302 defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER),
303 encode: getInnermostProperty(propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS),
304 htmlAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.HTML, propsList),
305 linkTags: getTagsFromPropsList(TAG_NAMES.LINK, [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF], propsList),
306 metaTags: getTagsFromPropsList(TAG_NAMES.META, [TAG_PROPERTIES.NAME, TAG_PROPERTIES.CHARSET, TAG_PROPERTIES.HTTPEQUIV, TAG_PROPERTIES.PROPERTY, TAG_PROPERTIES.ITEM_PROP], propsList),
307 noscriptTags: getTagsFromPropsList(TAG_NAMES.NOSCRIPT, [TAG_PROPERTIES.INNER_HTML], propsList),
308 onChangeClientState: getOnChangeClientState(propsList),
309 scriptTags: getTagsFromPropsList(TAG_NAMES.SCRIPT, [TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML], propsList),
310 styleTags: getTagsFromPropsList(TAG_NAMES.STYLE, [TAG_PROPERTIES.CSS_TEXT], propsList),
311 title: getTitleFromPropsList(propsList),
312 titleAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.TITLE, propsList)
313 };
314};
315
316var rafPolyfill = function () {
317 var clock = Date.now();
318
319 return function (callback) {
320 var currentTime = Date.now();
321
322 if (currentTime - clock > 16) {
323 clock = currentTime;
324 callback(currentTime);
325 } else {
326 setTimeout(function () {
327 rafPolyfill(callback);
328 }, 0);
329 }
330 };
331}();
332
333var cafPolyfill = function cafPolyfill(id) {
334 return clearTimeout(id);
335};
336
337var requestAnimationFrame = typeof window !== "undefined" ? window.requestAnimationFrame && window.requestAnimationFrame.bind(window) || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || rafPolyfill : global.requestAnimationFrame || rafPolyfill;
338
339var cancelAnimationFrame = typeof window !== "undefined" ? window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || cafPolyfill : global.cancelAnimationFrame || cafPolyfill;
340
341var warn = function warn(msg) {
342 return console && typeof console.warn === "function" && console.warn(msg);
343};
344
345var _helmetCallback = null;
346
347var handleClientStateChange = function handleClientStateChange(newState) {
348 if (_helmetCallback) {
349 cancelAnimationFrame(_helmetCallback);
350 }
351
352 if (newState.defer) {
353 _helmetCallback = requestAnimationFrame(function () {
354 commitTagChanges(newState, function () {
355 _helmetCallback = null;
356 });
357 });
358 } else {
359 commitTagChanges(newState);
360 _helmetCallback = null;
361 }
362};
363
364var commitTagChanges = function commitTagChanges(newState, cb) {
365 var baseTag = newState.baseTag,
366 bodyAttributes = newState.bodyAttributes,
367 htmlAttributes = newState.htmlAttributes,
368 linkTags = newState.linkTags,
369 metaTags = newState.metaTags,
370 noscriptTags = newState.noscriptTags,
371 onChangeClientState = newState.onChangeClientState,
372 scriptTags = newState.scriptTags,
373 styleTags = newState.styleTags,
374 title = newState.title,
375 titleAttributes = newState.titleAttributes;
376
377 updateAttributes(TAG_NAMES.BODY, bodyAttributes);
378 updateAttributes(TAG_NAMES.HTML, htmlAttributes);
379
380 updateTitle(title, titleAttributes);
381
382 var tagUpdates = {
383 baseTag: updateTags(TAG_NAMES.BASE, baseTag),
384 linkTags: updateTags(TAG_NAMES.LINK, linkTags),
385 metaTags: updateTags(TAG_NAMES.META, metaTags),
386 noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscriptTags),
387 scriptTags: updateTags(TAG_NAMES.SCRIPT, scriptTags),
388 styleTags: updateTags(TAG_NAMES.STYLE, styleTags)
389 };
390
391 var addedTags = {};
392 var removedTags = {};
393
394 Object.keys(tagUpdates).forEach(function (tagType) {
395 var _tagUpdates$tagType = tagUpdates[tagType],
396 newTags = _tagUpdates$tagType.newTags,
397 oldTags = _tagUpdates$tagType.oldTags;
398
399
400 if (newTags.length) {
401 addedTags[tagType] = newTags;
402 }
403 if (oldTags.length) {
404 removedTags[tagType] = tagUpdates[tagType].oldTags;
405 }
406 });
407
408 cb && cb();
409
410 onChangeClientState(newState, addedTags, removedTags);
411};
412
413var flattenArray = function flattenArray(possibleArray) {
414 return Array.isArray(possibleArray) ? possibleArray.join("") : possibleArray;
415};
416
417var updateTitle = function updateTitle(title, attributes) {
418 if (typeof title !== "undefined" && document.title !== title) {
419 document.title = flattenArray(title);
420 }
421
422 updateAttributes(TAG_NAMES.TITLE, attributes);
423};
424
425var updateAttributes = function updateAttributes(tagName, attributes) {
426 var elementTag = document.getElementsByTagName(tagName)[0];
427
428 if (!elementTag) {
429 return;
430 }
431
432 var helmetAttributeString = elementTag.getAttribute(HELMET_ATTRIBUTE);
433 var helmetAttributes = helmetAttributeString ? helmetAttributeString.split(",") : [];
434 var attributesToRemove = [].concat(helmetAttributes);
435 var attributeKeys = Object.keys(attributes);
436
437 for (var i = 0; i < attributeKeys.length; i++) {
438 var attribute = attributeKeys[i];
439 var value = attributes[attribute] || "";
440
441 if (elementTag.getAttribute(attribute) !== value) {
442 elementTag.setAttribute(attribute, value);
443 }
444
445 if (helmetAttributes.indexOf(attribute) === -1) {
446 helmetAttributes.push(attribute);
447 }
448
449 var indexToSave = attributesToRemove.indexOf(attribute);
450 if (indexToSave !== -1) {
451 attributesToRemove.splice(indexToSave, 1);
452 }
453 }
454
455 for (var _i = attributesToRemove.length - 1; _i >= 0; _i--) {
456 elementTag.removeAttribute(attributesToRemove[_i]);
457 }
458
459 if (helmetAttributes.length === attributesToRemove.length) {
460 elementTag.removeAttribute(HELMET_ATTRIBUTE);
461 } else if (elementTag.getAttribute(HELMET_ATTRIBUTE) !== attributeKeys.join(",")) {
462 elementTag.setAttribute(HELMET_ATTRIBUTE, attributeKeys.join(","));
463 }
464};
465
466var updateTags = function updateTags(type, tags) {
467 var headElement = document.head || document.querySelector(TAG_NAMES.HEAD);
468 var tagNodes = headElement.querySelectorAll(type + "[" + HELMET_ATTRIBUTE + "]");
469 var oldTags = Array.prototype.slice.call(tagNodes);
470 var newTags = [];
471 var indexToDelete = void 0;
472
473 if (tags && tags.length) {
474 tags.forEach(function (tag) {
475 var newElement = document.createElement(type);
476
477 for (var attribute in tag) {
478 if (tag.hasOwnProperty(attribute)) {
479 if (attribute === TAG_PROPERTIES.INNER_HTML) {
480 newElement.innerHTML = tag.innerHTML;
481 } else if (attribute === TAG_PROPERTIES.CSS_TEXT) {
482 if (newElement.styleSheet) {
483 newElement.styleSheet.cssText = tag.cssText;
484 } else {
485 newElement.appendChild(document.createTextNode(tag.cssText));
486 }
487 } else {
488 var value = typeof tag[attribute] === "undefined" ? "" : tag[attribute];
489 newElement.setAttribute(attribute, value);
490 }
491 }
492 }
493
494 newElement.setAttribute(HELMET_ATTRIBUTE, "true");
495
496 // Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
497 if (oldTags.some(function (existingTag, index) {
498 indexToDelete = index;
499 return newElement.isEqualNode(existingTag);
500 })) {
501 oldTags.splice(indexToDelete, 1);
502 } else {
503 newTags.push(newElement);
504 }
505 });
506 }
507
508 oldTags.forEach(function (tag) {
509 return tag.parentNode.removeChild(tag);
510 });
511 newTags.forEach(function (tag) {
512 return headElement.appendChild(tag);
513 });
514
515 return {
516 oldTags: oldTags,
517 newTags: newTags
518 };
519};
520
521var generateElementAttributesAsString = function generateElementAttributesAsString(attributes) {
522 return Object.keys(attributes).reduce(function (str, key) {
523 var attr = typeof attributes[key] !== "undefined" ? key + "=\"" + attributes[key] + "\"" : "" + key;
524 return str ? str + " " + attr : attr;
525 }, "");
526};
527
528var generateTitleAsString = function generateTitleAsString(type, title, attributes, encode) {
529 var attributeString = generateElementAttributesAsString(attributes);
530 var flattenedTitle = flattenArray(title);
531 return attributeString ? "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\" " + attributeString + ">" + encodeSpecialCharacters(flattenedTitle, encode) + "</" + type + ">" : "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\">" + encodeSpecialCharacters(flattenedTitle, encode) + "</" + type + ">";
532};
533
534var generateTagsAsString = function generateTagsAsString(type, tags, encode) {
535 return tags.reduce(function (str, tag) {
536 var attributeHtml = Object.keys(tag).filter(function (attribute) {
537 return !(attribute === TAG_PROPERTIES.INNER_HTML || attribute === TAG_PROPERTIES.CSS_TEXT);
538 }).reduce(function (string, attribute) {
539 var attr = typeof tag[attribute] === "undefined" ? attribute : attribute + "=\"" + encodeSpecialCharacters(tag[attribute], encode) + "\"";
540 return string ? string + " " + attr : attr;
541 }, "");
542
543 var tagContent = tag.innerHTML || tag.cssText || "";
544
545 var isSelfClosing = SELF_CLOSING_TAGS.indexOf(type) === -1;
546
547 return str + "<" + type + " " + HELMET_ATTRIBUTE + "=\"true\" " + attributeHtml + (isSelfClosing ? "/>" : ">" + tagContent + "</" + type + ">");
548 }, "");
549};
550
551var convertElementAttributestoReactProps = function convertElementAttributestoReactProps(attributes) {
552 var initProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
553
554 return Object.keys(attributes).reduce(function (obj, key) {
555 obj[REACT_TAG_MAP[key] || key] = attributes[key];
556 return obj;
557 }, initProps);
558};
559
560var convertReactPropstoHtmlAttributes = function convertReactPropstoHtmlAttributes(props) {
561 var initAttributes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
562
563 return Object.keys(props).reduce(function (obj, key) {
564 obj[HTML_TAG_MAP[key] || key] = props[key];
565 return obj;
566 }, initAttributes);
567};
568
569var generateTitleAsReactComponent = function generateTitleAsReactComponent(type, title, attributes) {
570 var _initProps;
571
572 // assigning into an array to define toString function on it
573 var initProps = (_initProps = {
574 key: title
575 }, _initProps[HELMET_ATTRIBUTE] = true, _initProps);
576 var props = convertElementAttributestoReactProps(attributes, initProps);
577
578 return [React.createElement(TAG_NAMES.TITLE, props, title)];
579};
580
581var generateTagsAsReactComponent = function generateTagsAsReactComponent(type, tags) {
582 return tags.map(function (tag, i) {
583 var _mappedTag;
584
585 var mappedTag = (_mappedTag = {
586 key: i
587 }, _mappedTag[HELMET_ATTRIBUTE] = true, _mappedTag);
588
589 Object.keys(tag).forEach(function (attribute) {
590 var mappedAttribute = REACT_TAG_MAP[attribute] || attribute;
591
592 if (mappedAttribute === TAG_PROPERTIES.INNER_HTML || mappedAttribute === TAG_PROPERTIES.CSS_TEXT) {
593 var content = tag.innerHTML || tag.cssText;
594 mappedTag.dangerouslySetInnerHTML = { __html: content };
595 } else {
596 mappedTag[mappedAttribute] = tag[attribute];
597 }
598 });
599
600 return React.createElement(type, mappedTag);
601 });
602};
603
604var getMethodsForTag = function getMethodsForTag(type, tags, encode) {
605 switch (type) {
606 case TAG_NAMES.TITLE:
607 return {
608 toComponent: function toComponent() {
609 return generateTitleAsReactComponent(type, tags.title, tags.titleAttributes, encode);
610 },
611 toString: function toString() {
612 return generateTitleAsString(type, tags.title, tags.titleAttributes, encode);
613 }
614 };
615 case ATTRIBUTE_NAMES.BODY:
616 case ATTRIBUTE_NAMES.HTML:
617 return {
618 toComponent: function toComponent() {
619 return convertElementAttributestoReactProps(tags);
620 },
621 toString: function toString() {
622 return generateElementAttributesAsString(tags);
623 }
624 };
625 default:
626 return {
627 toComponent: function toComponent() {
628 return generateTagsAsReactComponent(type, tags);
629 },
630 toString: function toString() {
631 return generateTagsAsString(type, tags, encode);
632 }
633 };
634 }
635};
636
637var mapStateOnServer = function mapStateOnServer(_ref) {
638 var baseTag = _ref.baseTag,
639 bodyAttributes = _ref.bodyAttributes,
640 encode = _ref.encode,
641 htmlAttributes = _ref.htmlAttributes,
642 linkTags = _ref.linkTags,
643 metaTags = _ref.metaTags,
644 noscriptTags = _ref.noscriptTags,
645 scriptTags = _ref.scriptTags,
646 styleTags = _ref.styleTags,
647 _ref$title = _ref.title,
648 title = _ref$title === undefined ? "" : _ref$title,
649 titleAttributes = _ref.titleAttributes;
650 return {
651 base: getMethodsForTag(TAG_NAMES.BASE, baseTag, encode),
652 bodyAttributes: getMethodsForTag(ATTRIBUTE_NAMES.BODY, bodyAttributes, encode),
653 htmlAttributes: getMethodsForTag(ATTRIBUTE_NAMES.HTML, htmlAttributes, encode),
654 link: getMethodsForTag(TAG_NAMES.LINK, linkTags, encode),
655 meta: getMethodsForTag(TAG_NAMES.META, metaTags, encode),
656 noscript: getMethodsForTag(TAG_NAMES.NOSCRIPT, noscriptTags, encode),
657 script: getMethodsForTag(TAG_NAMES.SCRIPT, scriptTags, encode),
658 style: getMethodsForTag(TAG_NAMES.STYLE, styleTags, encode),
659 title: getMethodsForTag(TAG_NAMES.TITLE, { title: title, titleAttributes: titleAttributes }, encode)
660 };
661};
662
663var Helmet = function Helmet(Component) {
664 var _class, _temp;
665
666 return _temp = _class = function (_React$Component) {
667 inherits(HelmetWrapper, _React$Component);
668
669 function HelmetWrapper() {
670 classCallCheck(this, HelmetWrapper);
671 return possibleConstructorReturn(this, _React$Component.apply(this, arguments));
672 }
673
674 HelmetWrapper.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps) {
675 return !isEqual(this.props, nextProps);
676 };
677
678 HelmetWrapper.prototype.mapNestedChildrenToProps = function mapNestedChildrenToProps(child, nestedChildren) {
679 if (!nestedChildren) {
680 return null;
681 }
682
683 switch (child.type) {
684 case TAG_NAMES.SCRIPT:
685 case TAG_NAMES.NOSCRIPT:
686 return {
687 innerHTML: nestedChildren
688 };
689
690 case TAG_NAMES.STYLE:
691 return {
692 cssText: nestedChildren
693 };
694 }
695
696 throw new Error("<" + child.type + " /> elements are self-closing and can not contain children. Refer to our API for more information.");
697 };
698
699 HelmetWrapper.prototype.flattenArrayTypeChildren = function flattenArrayTypeChildren(_ref) {
700 var _babelHelpers$extends;
701
702 var child = _ref.child,
703 arrayTypeChildren = _ref.arrayTypeChildren,
704 newChildProps = _ref.newChildProps,
705 nestedChildren = _ref.nestedChildren;
706
707 return _extends({}, arrayTypeChildren, (_babelHelpers$extends = {}, _babelHelpers$extends[child.type] = [].concat(arrayTypeChildren[child.type] || [], [_extends({}, newChildProps, this.mapNestedChildrenToProps(child, nestedChildren))]), _babelHelpers$extends));
708 };
709
710 HelmetWrapper.prototype.mapObjectTypeChildren = function mapObjectTypeChildren(_ref2) {
711 var _babelHelpers$extends2, _babelHelpers$extends3;
712
713 var child = _ref2.child,
714 newProps = _ref2.newProps,
715 newChildProps = _ref2.newChildProps,
716 nestedChildren = _ref2.nestedChildren;
717
718 switch (child.type) {
719 case TAG_NAMES.TITLE:
720 return _extends({}, newProps, (_babelHelpers$extends2 = {}, _babelHelpers$extends2[child.type] = nestedChildren, _babelHelpers$extends2.titleAttributes = _extends({}, newChildProps), _babelHelpers$extends2));
721
722 case TAG_NAMES.BODY:
723 return _extends({}, newProps, {
724 bodyAttributes: _extends({}, newChildProps)
725 });
726
727 case TAG_NAMES.HTML:
728 return _extends({}, newProps, {
729 htmlAttributes: _extends({}, newChildProps)
730 });
731 }
732
733 return _extends({}, newProps, (_babelHelpers$extends3 = {}, _babelHelpers$extends3[child.type] = _extends({}, newChildProps), _babelHelpers$extends3));
734 };
735
736 HelmetWrapper.prototype.mapArrayTypeChildrenToProps = function mapArrayTypeChildrenToProps(arrayTypeChildren, newProps) {
737 var newFlattenedProps = _extends({}, newProps);
738
739 Object.keys(arrayTypeChildren).forEach(function (arrayChildName) {
740 var _babelHelpers$extends4;
741
742 newFlattenedProps = _extends({}, newFlattenedProps, (_babelHelpers$extends4 = {}, _babelHelpers$extends4[arrayChildName] = arrayTypeChildren[arrayChildName], _babelHelpers$extends4));
743 });
744
745 return newFlattenedProps;
746 };
747
748 HelmetWrapper.prototype.warnOnInvalidChildren = function warnOnInvalidChildren(child, nestedChildren) {
749 if (process.env.NODE_ENV !== "production") {
750 if (!VALID_TAG_NAMES.some(function (name) {
751 return child.type === name;
752 })) {
753 if (typeof child.type === "function") {
754 return warn("You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.");
755 }
756
757 return warn("Only elements types " + VALID_TAG_NAMES.join(", ") + " are allowed. Helmet does not support rendering <" + child.type + "> elements. Refer to our API for more information.");
758 }
759
760 if (nestedChildren && typeof nestedChildren !== "string" && (!Array.isArray(nestedChildren) || nestedChildren.some(function (nestedChild) {
761 return typeof nestedChild !== "string";
762 }))) {
763 throw new Error("Helmet expects a string as a child of <" + child.type + ">. Did you forget to wrap your children in braces? ( <" + child.type + ">{``}</" + child.type + "> ) Refer to our API for more information.");
764 }
765 }
766
767 return true;
768 };
769
770 HelmetWrapper.prototype.mapChildrenToProps = function mapChildrenToProps(children, newProps) {
771 var _this2 = this;
772
773 var arrayTypeChildren = {};
774
775 React.Children.forEach(children, function (child) {
776 if (!child || !child.props) {
777 return;
778 }
779
780 var _child$props = child.props,
781 nestedChildren = _child$props.children,
782 childProps = objectWithoutProperties(_child$props, ["children"]);
783
784 var newChildProps = convertReactPropstoHtmlAttributes(childProps);
785
786 _this2.warnOnInvalidChildren(child, nestedChildren);
787
788 switch (child.type) {
789 case TAG_NAMES.LINK:
790 case TAG_NAMES.META:
791 case TAG_NAMES.NOSCRIPT:
792 case TAG_NAMES.SCRIPT:
793 case TAG_NAMES.STYLE:
794 arrayTypeChildren = _this2.flattenArrayTypeChildren({
795 child: child,
796 arrayTypeChildren: arrayTypeChildren,
797 newChildProps: newChildProps,
798 nestedChildren: nestedChildren
799 });
800 break;
801
802 default:
803 newProps = _this2.mapObjectTypeChildren({
804 child: child,
805 newProps: newProps,
806 newChildProps: newChildProps,
807 nestedChildren: nestedChildren
808 });
809 break;
810 }
811 });
812
813 newProps = this.mapArrayTypeChildrenToProps(arrayTypeChildren, newProps);
814 return newProps;
815 };
816
817 HelmetWrapper.prototype.render = function render() {
818 var _props = this.props,
819 children = _props.children,
820 props = objectWithoutProperties(_props, ["children"]);
821
822 var newProps = _extends({}, props);
823
824 if (children) {
825 newProps = this.mapChildrenToProps(children, newProps);
826 }
827
828 return React.createElement(Component, newProps);
829 };
830
831 createClass(HelmetWrapper, null, [{
832 key: "canUseDOM",
833
834
835 // Component.peek comes from react-side-effect:
836 // For testing, you may use a static peek() method available on the returned component.
837 // It lets you get the current state without resetting the mounted instance stack.
838 // Don’t use it for anything other than testing.
839
840 /**
841 * @param {Object} base: {"target": "_blank", "href": "http://mysite.com/"}
842 * @param {Object} bodyAttributes: {"className": "root"}
843 * @param {String} defaultTitle: "Default Title"
844 * @param {Boolean} defer: true
845 * @param {Boolean} encodeSpecialCharacters: true
846 * @param {Object} htmlAttributes: {"lang": "en", "amp": undefined}
847 * @param {Array} link: [{"rel": "canonical", "href": "http://mysite.com/example"}]
848 * @param {Array} meta: [{"name": "description", "content": "Test description"}]
849 * @param {Array} noscript: [{"innerHTML": "<img src='http://mysite.com/js/test.js'"}]
850 * @param {Function} onChangeClientState: "(newState) => console.log(newState)"
851 * @param {Array} script: [{"type": "text/javascript", "src": "http://mysite.com/js/test.js"}]
852 * @param {Array} style: [{"type": "text/css", "cssText": "div { display: block; color: blue; }"}]
853 * @param {String} title: "Title"
854 * @param {Object} titleAttributes: {"itemprop": "name"}
855 * @param {String} titleTemplate: "MySite.com - %s"
856 */
857 set: function set$$1(canUseDOM) {
858 Component.canUseDOM = canUseDOM;
859 }
860 }]);
861 return HelmetWrapper;
862 }(React.Component), _class.propTypes = {
863 base: PropTypes.object,
864 bodyAttributes: PropTypes.object,
865 children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
866 defaultTitle: PropTypes.string,
867 defer: PropTypes.bool,
868 encodeSpecialCharacters: PropTypes.bool,
869 htmlAttributes: PropTypes.object,
870 link: PropTypes.arrayOf(PropTypes.object),
871 meta: PropTypes.arrayOf(PropTypes.object),
872 noscript: PropTypes.arrayOf(PropTypes.object),
873 onChangeClientState: PropTypes.func,
874 script: PropTypes.arrayOf(PropTypes.object),
875 style: PropTypes.arrayOf(PropTypes.object),
876 title: PropTypes.string,
877 titleAttributes: PropTypes.object,
878 titleTemplate: PropTypes.string
879 }, _class.defaultProps = {
880 defer: true,
881 encodeSpecialCharacters: true
882 }, _class.peek = Component.peek, _class.rewind = function () {
883 var mappedState = Component.rewind();
884 if (!mappedState) {
885 // provide fallback if mappedState is undefined
886 mappedState = mapStateOnServer({
887 baseTag: [],
888 bodyAttributes: {},
889 encodeSpecialCharacters: true,
890 htmlAttributes: {},
891 linkTags: [],
892 metaTags: [],
893 noscriptTags: [],
894 scriptTags: [],
895 styleTags: [],
896 title: "",
897 titleAttributes: {}
898 });
899 }
900
901 return mappedState;
902 }, _temp;
903};
904
905var NullComponent = function NullComponent() {
906 return null;
907};
908
909var HelmetSideEffects = withSideEffect(reducePropsToState, handleClientStateChange, mapStateOnServer)(NullComponent);
910
911var HelmetExport = Helmet(HelmetSideEffects);
912HelmetExport.renderStatic = HelmetExport.rewind;
913
914export default HelmetExport;
915export { HelmetExport as Helmet };