From 1edb976e395056e674645d516d79d95ee3ede7ea Mon Sep 17 00:00:00 2001 From: Sudhanshu Yadav <sudhanshuyadav2@gmail.com> Date: Sun, 30 Sep 2018 12:41:29 +0530 Subject: [PATCH] - Added getTags method on MetaTagsServer, which returns React elements instead of string. - Code refactor of server utils to remove duplicate. - Change deserver port to 9010 for examples webpack dev server --- README.md | 97 +++++++++++++++++++------------ dist/react-meta-tags.es.js | 13 ++++- dist/react-meta-tags.js | 13 ++++- dist/react-meta-tags.min.js | 2 +- examples/app.js | 111 ++++++++++++++++++++++-------------- lib/meta_tags_server.js | 27 ++++----- lib/utils.js | 88 ++++++++++++---------------- package.json | 4 +- src/meta_tags_server.js | 30 +++++----- src/utils.js | 79 +++++++++---------------- webpack.dev.config.js | 2 +- 11 files changed, 245 insertions(+), 221 deletions(-) diff --git a/README.md b/README.md index f9ba299..0ce4181 100644 --- a/README.md +++ b/README.md @@ -68,37 +68,37 @@ import {MetaTagsContext} from 'react-meta-tags'; */ app.use((req, res) => { - //make sure you get a new metatags instance for each request - const metaTagsInstance = MetaTagsServer(); - - //react router match - match({ - routes, location: req.url - }, (error, redirectLocation, renderProps) => { - let reactString; - - try{ - reactString = ReactDomServer.renderToString( - <Provider store={store}> {/*** If you are using redux ***/} - {/* You have to pass extract method through MetaTagsContext so it can catch meta tags */} - <MetaTagsContext extract = {metaTagsInstance.extract}> - <RouterContext {...renderProps}/> - </MetaTagsContext> - </Provider> - ); - } - catch(e){ - res.status(500).send(e.stack); - return; - } - - //get all title and metatags as string - const meta = metaTagsInstance.renderToString(); - - //append metatag string to your template - const template = (` - <!doctype html> - <html lang="en-us"> + //make sure you get a new metatags instance for each request + const metaTagsInstance = MetaTagsServer(); + + //react router match + match({ + routes, location: req.url + }, (error, redirectLocation, renderProps) => { + let reactString; + + try{ + reactString = ReactDomServer.renderToString( + <Provider store={store}> {/*** If you are using redux ***/} + {/* You have to pass extract method through MetaTagsContext so it can catch meta tags */} + <MetaTagsContext extract = {metaTagsInstance.extract}> + <RouterContext {...renderProps}/> + </MetaTagsContext> + </Provider> + ); + } + catch(e){ + res.status(500).send(e.stack); + return; + } + + //get all title and metatags as string + const meta = metaTagsInstance.renderToString(); + + //append metatag string to your layout + const htmlStr = (` + <!doctype html> + <html lang="en-us"> <head> <meta charSet="utf-8"/> ${meta} @@ -107,11 +107,12 @@ app.use((req, res) => { <div id="content"> ${reactString} </div> - </body> - `); + </body> + </html> + `); - res.status(200).send(template); - }); + res.status(200).send(layout); + }); }); ``` @@ -123,6 +124,32 @@ So as per above code we have to do following for server rendering 4. Extract meta string using renderToString of MetaTagsServer instance 5. Append meta string to your html template. +#### JSX Layout +You might also be using React to define your layout, in which case you can use `getTags` method from `metaTagsInstance`. The layout part of above server side example will look like this. +```jsx +//get all title and metatags as React elements +const metaTags = metaTagsInstance.getTags(); + +//append metatag string to your layout +const layout = ( + <html lang="en-us"> + <head> + <meta charSet="utf-8"/> + {metaTags} + </head> + <body> + <div id="app" dangerouslySetInnerHTML={{__html: reactString}} /> + </body> + </html> +); + +const htmlStr = ReactDomServer.renderToString(layout); + +res.status(200).send(htmlStr); +``` + + + ## Meta Tag Uniqueness - The module uniquely identifies meta tag by id / property / name / itemProp attribute. - Multiple meta tags with same property / name is valid in html. If you need such case. Define a different id to both so that it can be uniquely differentiate. diff --git a/dist/react-meta-tags.es.js b/dist/react-meta-tags.es.js index 859f884..0e9cb83 100644 --- a/dist/react-meta-tags.es.js +++ b/dist/react-meta-tags.es.js @@ -1,5 +1,5 @@ /** - * react-meta-tags - 0.6.0 + * react-meta-tags - 0.7.0 * Author : Sudhanshu Yadav * Copyright (c) 2016, 2018 to Sudhanshu Yadav, released under the MIT license. * https://github.com/s-yadav/react-meta-tags @@ -292,7 +292,13 @@ _defineProperty(MetaTagsContext, "childContextTypes", { extract: propTypes.func }); -var uniqueIdentifiers = ['property', 'name', 'itemprop']; +var uniqueIdentifiersI = ['property', 'name', 'itemprop']; +/** + Note: + 1. In server side we will add meta tags and title at last after fitering + 2. In client we will match and replace meta tagString + 3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement +**/ function filterOutMetaWithId(metas) { metas = Array.prototype.slice.call(metas || []); @@ -300,6 +306,7 @@ function filterOutMetaWithId(metas) { return !meta.id; }); } + function getDuplicateTitle() { return document.head.querySelectorAll('title'); } @@ -315,7 +322,7 @@ function getDuplicateMeta(meta) { } //for any other unique identifier check if metas already available with same identifier which doesn't have id - return uniqueIdentifiers.reduce(function (duplicates, identifier) { + return uniqueIdentifiersI.reduce(function (duplicates, identifier) { var identifierValue = meta.getAttribute(identifier); return identifierValue ? duplicates.concat(filterOutMetaWithId(head.querySelectorAll("[".concat(identifier, " = \"").concat(identifierValue, "\"]")))) : duplicates; }, []); diff --git a/dist/react-meta-tags.js b/dist/react-meta-tags.js index 803434a..bfca068 100644 --- a/dist/react-meta-tags.js +++ b/dist/react-meta-tags.js @@ -1,5 +1,5 @@ /** - * react-meta-tags - 0.6.0 + * react-meta-tags - 0.7.0 * Author : Sudhanshu Yadav * Copyright (c) 2016, 2018 to Sudhanshu Yadav, released under the MIT license. * https://github.com/s-yadav/react-meta-tags @@ -298,7 +298,13 @@ extract: propTypes.func }); - var uniqueIdentifiers = ['property', 'name', 'itemprop']; + var uniqueIdentifiersI = ['property', 'name', 'itemprop']; + /** + Note: + 1. In server side we will add meta tags and title at last after fitering + 2. In client we will match and replace meta tagString + 3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement + **/ function filterOutMetaWithId(metas) { metas = Array.prototype.slice.call(metas || []); @@ -306,6 +312,7 @@ return !meta.id; }); } + function getDuplicateTitle() { return document.head.querySelectorAll('title'); } @@ -321,7 +328,7 @@ } //for any other unique identifier check if metas already available with same identifier which doesn't have id - return uniqueIdentifiers.reduce(function (duplicates, identifier) { + return uniqueIdentifiersI.reduce(function (duplicates, identifier) { var identifierValue = meta.getAttribute(identifier); return identifierValue ? duplicates.concat(filterOutMetaWithId(head.querySelectorAll("[".concat(identifier, " = \"").concat(identifierValue, "\"]")))) : duplicates; }, []); diff --git a/dist/react-meta-tags.min.js b/dist/react-meta-tags.min.js index 823ccfb..cdcf5f0 100644 --- a/dist/react-meta-tags.min.js +++ b/dist/react-meta-tags.min.js @@ -1,5 +1,5 @@ /** - * react-meta-tags - 0.6.0 + * react-meta-tags - 0.7.0 * Author : Sudhanshu Yadav * Copyright (c) 2016, 2018 to Sudhanshu Yadav, released under the MIT license. * https://github.com/s-yadav/react-meta-tags diff --git a/examples/app.js b/examples/app.js index 2713ca6..ac8ef5e 100644 --- a/examples/app.js +++ b/examples/app.js @@ -8,7 +8,53 @@ import {MetaTagsContext} from '../src/index'; import routes from './shared/routes'; -const app = express(); +const app = express() + +function renderToString(metaTagsInstance, reactString) { + const meta = metaTagsInstance.renderToString(); + + const html = ` + <html lang="en"> + <head> + <meta charSet="utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <meta http-equiv="x-ua-compatible" content="ie=edge"> + + ${meta} + <!-- Bootstrap CSS --> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" integrity="2hfp1SzUoho7/TsGGGDaFdsuuDL0LX2hnUp6VkX3CUQ2K4K+xjboZdsXyp4oUHZj" crossorigin="anonymous"> + + </head> + <body> + <div id="app">${reactString}</div> + <script src="http://localhost:9010/bundle.js"></script> + </body> + </html> + `; + + return html; +} + +function renderToStringFromJSx(metaTagsInstance, reactString) { + const tags = metaTagsInstance.getTags(); + const wholeHtml = ( + <html lang="en"> + <head> + <meta charSet="utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> + <meta httpEquiv="x-ua-compatible" content="ie=edge" /> + {tags} + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" integrity="2hfp1SzUoho7/TsGGGDaFdsuuDL0LX2hnUp6VkX3CUQ2K4K+xjboZdsXyp4oUHZj" crossOrigin="anonymous" /> + </head> + <body> + <div id="app" dangerouslySetInnerHTML={{__html: reactString}} /> + <script src="http://localhost:9010/bundle.js"></script> + </body> + </html> + ); + + return ReactDomServer.renderToString(wholeHtml) +} app.use((req, res) => { match({ @@ -19,46 +65,27 @@ app.use((req, res) => { } else if (redirectLocation) { res.redirect(302, redirectLocation.pathname + redirectLocation.search) } else if (renderProps) { - let reactString; - - const metaTagsInstance = MetaTagsServer(); - - try{ - reactString = ReactDomServer.renderToString( - <MetaTagsContext extract = {metaTagsInstance.extract}> - <RouterContext {...renderProps}/> - </MetaTagsContext> - ); - } - catch(e){ - console.log(e); - res.status(500).send(e.stack); - return; - } - - //const meta = metaTagsInstance.renderToString(); - const meta = ''; - - const template = ` - <!doctype html> - <html lang="en"> - <head> - <meta charSet="utf-8"/> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <meta http-equiv="x-ua-compatible" content="ie=edge"> - - ${meta} - <!-- Bootstrap CSS --> - <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" integrity="2hfp1SzUoho7/TsGGGDaFdsuuDL0LX2hnUp6VkX3CUQ2K4K+xjboZdsXyp4oUHZj" crossorigin="anonymous"> - - </head> - <body> - <div id="app">${reactString}</div> - <script src="http://localhost:9000/bundle.js"></script> - </body> - `; - - res.status(200).send(template) + let reactString; + + const metaTagsInstance = MetaTagsServer(); + + try{ + reactString = ReactDomServer.renderToString( + <MetaTagsContext extract = {metaTagsInstance.extract}> + <RouterContext {...renderProps}/> + </MetaTagsContext> + ); + } + catch(e){ + console.log(e); + res.status(500).send(e.stack); + return; + } + + //const html = renderToString(metaTagsInstance, reactString); + const html = renderToStringFromJSx(metaTagsInstance, reactString); + + res.status(200).send(html); } else { console.log('redirected'); res.status(301).redirect('/') @@ -66,6 +93,6 @@ app.use((req, res) => { }); }); -app.listen(8070, function() { +app.listen(8070, () => { console.log('Listening on 8070'); }) diff --git a/lib/meta_tags_server.js b/lib/meta_tags_server.js index 3f29c5b..a03b74f 100644 --- a/lib/meta_tags_server.js +++ b/lib/meta_tags_server.js @@ -17,12 +17,18 @@ function MetaTagsServer() { var headElms = []; return { extract: function extract(elms) { - headElms.push(elms); + if (!(elms instanceof Array)) { + elms = [elms]; + } + + headElms = headElms.concat(elms); }, renderToString: function renderToString() { + var filteredElms = (0, _utils.filterAndArrangeTags)(headElms); + var headComponent = _react.default.createElement("div", { className: "react-head-temp" - }, headElms); + }, filteredElms); var componentStr = _server.default.renderToStaticMarkup(headComponent); //remove wrapper div from string @@ -30,19 +36,10 @@ function MetaTagsServer() { componentStr = componentStr.replace(/^<div[^<>]*class="react-head-temp"[^<>]*>(.*)<\/div>$/, function ($1, $2) { return $2; }); - - var _extractMetaAndTitle = (0, _utils.extractMetaAndTitle)(componentStr), - _extractMetaAndTitle$ = _extractMetaAndTitle.title, - title = _extractMetaAndTitle$ === void 0 ? '' : _extractMetaAndTitle$, - metas = _extractMetaAndTitle.metas, - _extractMetaAndTitle$2 = _extractMetaAndTitle.canonicalLink, - canonicalLink = _extractMetaAndTitle$2 === void 0 ? '' : _extractMetaAndTitle$2, - rest = _extractMetaAndTitle.rest; - - var metasStr = (0, _utils.removeDuplicateMetas)(metas).map(function (meta) { - return meta._tagString; - }).join(''); - return "\n ".concat(title, "\n ").concat(metasStr, "\n ").concat(canonicalLink, "\n ").concat(rest, "\n "); + return componentStr; + }, + getTags: function getTags() { + return (0, _utils.filterAndArrangeTags)(headElms); } }; } diff --git a/lib/utils.js b/lib/utils.js index 0443015..407de80 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -3,8 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.extractMetaAndTitle = extractMetaAndTitle; -exports.removeDuplicateMetas = removeDuplicateMetas; +exports.filterAndArrangeTags = filterAndArrangeTags; exports.getDuplicateTitle = getDuplicateTitle; exports.getDuplicateCanonical = getDuplicateCanonical; exports.getDuplicateMeta = getDuplicateMeta; @@ -12,15 +11,18 @@ exports.appendChild = appendChild; exports.removeChild = removeChild; exports.getDomAsString = getDomAsString; -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } + +function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } + +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } + +var camelCaseProps = ['itemProp']; +var uniqueIdentifiersI = ['property', 'name', 'itemprop']; +var uniqueIdentifiers = uniqueIdentifiersI.concat(camelCaseProps); //case sensitive props is defined in case anyone defined the lowercase prop -var metaRegex = /<meta[^<>]*?=(['"].*?['"]|[^<>]*?)*?\/?>/g; -var canonicalLinkRegex = /<link[^<>]*?rel=['"]canonical['"].*?(\/>|<\/link>)/g; -var titleRegex = /<title[^<>]*?>(.*?)<\/title>/g; -var attributesRegex = /(\S*?)=("(.*?)"|'(.*?)'|([^<>\s]*))/g; -var uniqueIdentifiers = ['property', 'name', 'itemprop']; var uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']); /** Note: @@ -29,19 +31,6 @@ var uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']); 3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement **/ -function getAttributes(tagString) { - var attr = {}; - if (!tagString) return attr; - var match = attributesRegex.exec(tagString); - - while (match !== null) { - attr[match[1].toLowerCase()] = match[3] || match[4] || match[5]; - match = attributesRegex.exec(tagString); - } - - return attr; -} - function filterOutMetaWithId(metas) { metas = Array.prototype.slice.call(metas || []); return metas.filter(function (meta) { @@ -49,32 +38,26 @@ function filterOutMetaWithId(metas) { }); } -function extractMetaAndTitle(domString) { - var title, canonicalLink; - var metas = []; //extract title, only take the last title, remove title from the string - - domString = domString.replace(titleRegex, function (titleStr) { - title = titleStr; - return ''; - }); //extract canonical - - domString = domString.replace(canonicalLinkRegex, function (canonicalLinkStr) { - canonicalLink = canonicalLinkStr; - return ''; - }); //extract metas - - domString = domString.replace(metaRegex, function (_tagString) { - metas.push(_objectSpread({}, getAttributes(_tagString), { - _tagString: _tagString - })); - return ''; +function filterAndArrangeTags(headElms) { + var title = null, + canonicalLink = null; + var metas = [], + rest = []; + headElms.forEach(function (elm) { + var type = elm.type, + props = elm.props; + + if (type === 'title') { + title = elm; + } else if (type === 'link' && props.rel === 'canonical') { + canonicalLink = elm; + } else if (type === 'meta') { + metas.push(elm); + } else { + rest.push(elm); + } }); - return { - title: title, - metas: metas, - canonicalLink: canonicalLink, - rest: domString - }; + return [title].concat(_toConsumableArray(removeDuplicateMetas(metas)), [canonicalLink], rest); } function removeDuplicateMetas(metas) { @@ -87,15 +70,16 @@ function removeDuplicateMetas(metas) { var _loop = function _loop(i) { var meta = metas[i]; - var id = meta.id; + var id = meta.props.id; var addMeta = false; //if has id and element with id is not present than always add meta if (id) { addMeta = !addedMeta.id[id]; //for any other unique identifier check if meta already available with same identifier which doesn't have id } else { addMeta = uniqueIdentifiers.filter(function (identifier) { - var existing = addedMeta[identifier][meta[identifier]]; - return existing && !existing.id; + var identifierValue = meta.props[identifier]; + var existing = addedMeta[identifier][identifierValue]; + return existing && !existing.props.id; }).length === 0; } @@ -103,7 +87,7 @@ function removeDuplicateMetas(metas) { filteredMetas.unshift(meta); //add meta as added uniqueIdentifiersAll.forEach(function (identifier) { - var identifierValue = meta[identifier]; + var identifierValue = meta.props[identifier]; if (identifierValue) addedMeta[identifier][identifierValue] = meta; }); } @@ -133,7 +117,7 @@ function getDuplicateMeta(meta) { } //for any other unique identifier check if metas already available with same identifier which doesn't have id - return uniqueIdentifiers.reduce(function (duplicates, identifier) { + return uniqueIdentifiersI.reduce(function (duplicates, identifier) { var identifierValue = meta.getAttribute(identifier); return identifierValue ? duplicates.concat(filterOutMetaWithId(head.querySelectorAll("[".concat(identifier, " = \"").concat(identifierValue, "\"]")))) : duplicates; }, []); diff --git a/package.json b/package.json index aaa52d5..d206220 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-meta-tags", "description": "Handle document meta/head tags in isomorphic react with ease.", - "version": "0.6.0", + "version": "0.7.0", "main": "lib/index.js", "author": "Sudhanshu Yadav", "license": "MIT", @@ -25,7 +25,7 @@ "react-meta-tags" ], "scripts": { - "start": "cross-env nodemon example.js | npm run start-dev", + "start": "cross-env npm run start-dev | nodemon example.js", "start-dev": "cross-env webpack-dev-server --content-base example/ --config webpack.dev.config.js --watch-poll --progress --inline --hot", "bundle": "cross-env NODE_ENV=production rollup -c rollup.config.js && npm run compile", "compile": "cross-env babel src --out-dir lib" diff --git a/src/meta_tags_server.js b/src/meta_tags_server.js index 136d343..8e53f8d 100644 --- a/src/meta_tags_server.js +++ b/src/meta_tags_server.js @@ -1,16 +1,20 @@ import React from 'react'; import ReactDomServer from 'react-dom/server'; -import {extractMetaAndTitle, removeDuplicateMetas} from './utils'; +import {filterAndArrangeTags} from './utils'; function MetaTagsServer(){ - const headElms = []; + let headElms = []; return { - extract: function(elms){ - headElms.push(elms); + extract(elms) { + if (!(elms instanceof Array)) { + elms = [elms]; + } + headElms = headElms.concat(elms); }, - renderToString: function(){ - const headComponent = <div className="react-head-temp">{headElms}</div>; + renderToString() { + const filteredElms = filterAndArrangeTags(headElms); + const headComponent = <div className="react-head-temp">{filteredElms}</div>; let componentStr = ReactDomServer.renderToStaticMarkup(headComponent); //remove wrapper div from string @@ -18,16 +22,10 @@ function MetaTagsServer(){ return $2; }); - const {title = '', metas, canonicalLink = '', rest} = extractMetaAndTitle(componentStr); - - const metasStr = removeDuplicateMetas(metas).map(meta => meta._tagString).join(''); - - return ` - ${title} - ${metasStr} - ${canonicalLink} - ${rest} - `; + return componentStr; + }, + getTags() { + return filterAndArrangeTags(headElms); } } } diff --git a/src/utils.js b/src/utils.js index c261ec4..8ee2a24 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,8 +1,6 @@ -const metaRegex = /<meta[^<>]*?=(['"].*?['"]|[^<>]*?)*?\/?>/g; -const canonicalLinkRegex = /<link[^<>]*?rel=['"]canonical['"].*?(\/>|<\/link>)/g; -const titleRegex = /<title[^<>]*?>(.*?)<\/title>/g; -const attributesRegex = /(\S*?)=("(.*?)"|'(.*?)'|([^<>\s]*))/g; -const uniqueIdentifiers = ['property', 'name', 'itemprop']; +const camelCaseProps = ['itemProp']; +const uniqueIdentifiersI = ['property', 'name', 'itemprop']; +const uniqueIdentifiers = uniqueIdentifiersI.concat(camelCaseProps); //case sensitive props is defined in case anyone defined the lowercase prop const uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']); /** @@ -12,56 +10,34 @@ const uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']); 3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement **/ -function getAttributes(tagString) { - const attr = {}; - if (!tagString) return attr; - let match = attributesRegex.exec(tagString); - while (match !== null) { - attr[match[1].toLowerCase()] = match[3] || match[4] || match[5]; - match = attributesRegex.exec(tagString); - } - - return attr; -} - function filterOutMetaWithId(metas) { metas = Array.prototype.slice.call(metas || []); return metas.filter(meta => !meta.id); } -export function extractMetaAndTitle(domString) { - let title, canonicalLink; - const metas = []; - - //extract title, only take the last title, remove title from the string - domString = domString.replace(titleRegex, (titleStr) => { - title = titleStr; - return ''; - }); - - //extract canonical - domString = domString.replace(canonicalLinkRegex, (canonicalLinkStr) => { - canonicalLink = canonicalLinkStr; - return ''; - }); - - - //extract metas - domString = domString.replace(metaRegex, (_tagString) => { - metas.push({...getAttributes(_tagString), _tagString}); - return ''; - }); - - return { - title, - metas, - canonicalLink, - rest: domString +export function filterAndArrangeTags(headElms) { + let title = null, canonicalLink = null; + const metas = [], rest = []; + + headElms.forEach((elm) => { + const { type, props } = elm; + if (type === 'title') { + title = elm; + } else if (type === 'link' && props.rel === 'canonical') { + canonicalLink = elm; + } else if (type === 'meta') { + metas.push(elm); + } else { + rest.push(elm); } + }); + + return [title, ...removeDuplicateMetas(metas), canonicalLink, ...rest]; } -export function removeDuplicateMetas(metas) { + +function removeDuplicateMetas(metas) { const addedMeta = {}; //initialize all the identifiers with empty array @@ -73,7 +49,7 @@ export function removeDuplicateMetas(metas) { for (let i = metas.length - 1; i >=0 ; i--) { const meta = metas[i]; - const { id } = meta; + const { id } = meta.props; let addMeta = false; //if has id and element with id is not present than always add meta @@ -83,8 +59,9 @@ export function removeDuplicateMetas(metas) { //for any other unique identifier check if meta already available with same identifier which doesn't have id } else { addMeta = uniqueIdentifiers.filter((identifier) => { - const existing = addedMeta[identifier][meta[identifier]]; - return existing && !existing.id; + const identifierValue = meta.props[identifier]; + const existing = addedMeta[identifier][identifierValue]; + return existing && !existing.props.id; }).length === 0; } @@ -93,7 +70,7 @@ export function removeDuplicateMetas(metas) { //add meta as added uniqueIdentifiersAll.forEach((identifier) => { - const identifierValue = meta[identifier]; + const identifierValue = meta.props[identifier]; if (identifierValue) addedMeta[identifier][identifierValue] = meta; }); } @@ -120,7 +97,7 @@ export function getDuplicateMeta(meta) { } //for any other unique identifier check if metas already available with same identifier which doesn't have id - return uniqueIdentifiers.reduce((duplicates, identifier) => { + return uniqueIdentifiersI.reduce((duplicates, identifier) => { const identifierValue = meta.getAttribute(identifier); return (identifierValue ? duplicates.concat(filterOutMetaWithId(head.querySelectorAll(`[${identifier} = "${identifierValue}"]`))) : diff --git a/webpack.dev.config.js b/webpack.dev.config.js index 37af3bf..f044548 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.js @@ -12,7 +12,7 @@ module.exports = { }, devServer: { //contentBase: path.join(__dirname, 'dist'), - port: 9000, + port: 9010, }, module: { rules: [ -- GitLab