diff --git a/src/client/refresh.js b/src/client/refresh.js index 8567fd0..3bfa33e 100644 --- a/src/client/refresh.js +++ b/src/client/refresh.js @@ -1,18 +1,20 @@ import getMetaInfo from '../shared/getMetaInfo' import updateClientMetaInfo from './updateClientMetaInfo' -/** - * When called, will update the current meta info with new meta info. - * Useful when updating meta info as the result of an asynchronous - * action that resolves after the initial render takes place. - * - * Credit to [Sébastien Chopin](https://github.com/Atinux) for the suggestion - * to implement this method. - * - * @return {Object} - new meta info - */ -export default function refresh () { - const info = getMetaInfo(this.$root) - updateClientMetaInfo(info) - return info +export default function _refresh (options) { + /** + * When called, will update the current meta info with new meta info. + * Useful when updating meta info as the result of an asynchronous + * action that resolves after the initial render takes place. + * + * Credit to [Sébastien Chopin](https://github.com/Atinux) for the suggestion + * to implement this method. + * + * @return {Object} - new meta info + */ + return function refresh () { + const info = getMetaInfo(options)(this.$root) + updateClientMetaInfo(options)(info) + return info + } } diff --git a/src/client/updateClientMetaInfo.js b/src/client/updateClientMetaInfo.js index f32e011..d6b1243 100644 --- a/src/client/updateClientMetaInfo.js +++ b/src/client/updateClientMetaInfo.js @@ -1,53 +1,56 @@ import updateTitle from './updaters/updateTitle' import updateTagAttributes from './updaters/updateTagAttributes' import updateTags from './updaters/updateTags' -import { SERVER_RENDERED_ATTRIBUTE } from '../shared/constants' -/** - * Performs client-side updates when new meta info is received - * - * @param {Object} newInfo - the meta info to update to - */ -export default function updateClientMetaInfo (newInfo) { - const htmlTag = document.getElementsByTagName('html')[0] - // if this is not a server render, then update - if (htmlTag.getAttribute(SERVER_RENDERED_ATTRIBUTE) === null) { - // initialize tracked changes - const addedTags = {} - const removedTags = {} +export default function _updateClientMetaInfo (options) { + const { ssrAttribute } = options - Object.keys(newInfo).forEach((key) => { - switch (key) { - // update the title - case 'title': - updateTitle(newInfo.title) - break - // update attributes - case 'htmlAttrs': - case 'bodyAttrs': - updateTagAttributes(newInfo[key], key === 'htmlAttrs' ? htmlTag : document.getElementsByTagName('body')[0]) - break - // ignore these - case 'titleChunk': - case 'titleTemplate': - case 'changed': - break - // catch-all update tags - default: - const { oldTags, newTags } = updateTags(key, newInfo[key], document.getElementsByTagName('head')[0]) - if (newTags.length) { - addedTags[key] = newTags - removedTags[key] = oldTags - } + /** + * Performs client-side updates when new meta info is received + * + * @param {Object} newInfo - the meta info to update to + */ + return function updateClientMetaInfo (newInfo) { + const htmlTag = document.getElementsByTagName('html')[0] + // if this is not a server render, then update + if (htmlTag.getAttribute(ssrAttribute) === null) { + // initialize tracked changes + const addedTags = {} + const removedTags = {} + + Object.keys(newInfo).forEach((key) => { + switch (key) { + // update the title + case 'title': + updateTitle(options)(newInfo.title) + break + // update attributes + case 'htmlAttrs': + case 'bodyAttrs': + updateTagAttributes(options)(newInfo[key], key === 'htmlAttrs' ? htmlTag : document.getElementsByTagName('body')[0]) + break + // ignore these + case 'titleChunk': + case 'titleTemplate': + case 'changed': + break + // catch-all update tags + default: + const { oldTags, newTags } = updateTags(options)(key, newInfo[key], document.getElementsByTagName('head')[0]) + if (newTags.length) { + addedTags[key] = newTags + removedTags[key] = oldTags + } + } + }) + + // emit "event" with new info + if (typeof newInfo.changed === 'function') { + newInfo.changed(newInfo, addedTags, removedTags) } - }) - - // emit "event" with new info - if (typeof newInfo.changed === 'function') { - newInfo.changed(newInfo, addedTags, removedTags) + } else { + // remove the server render attribute so we can update on changes + htmlTag.removeAttribute(ssrAttribute) } - } else { - // remove the server render attribute so we can update on changes - htmlTag.removeAttribute(SERVER_RENDERED_ATTRIBUTE) } } diff --git a/src/client/updaters/updateTagAttributes.js b/src/client/updaters/updateTagAttributes.js index cf19641..b413b5d 100644 --- a/src/client/updaters/updateTagAttributes.js +++ b/src/client/updaters/updateTagAttributes.js @@ -1,35 +1,35 @@ -import { VUE_META_ATTRIBUTE } from '../../shared/constants' - -/** - * updates the document's html tag attributes - * - * @param {Object} attrs - the new document html attributes - * @param {HTMLElement} tag - the HTMLElment tag to update with new attrs - */ -export default function updateTagAttributes (attrs, tag) { - const vueMetaAttrString = tag.getAttribute(VUE_META_ATTRIBUTE) - const vueMetaAttrs = vueMetaAttrString ? vueMetaAttrString.split(',') : [] - const toRemove = [].concat(vueMetaAttrs) - for (let attr in attrs) { - if (attrs.hasOwnProperty(attr)) { - const val = attrs[attr] || '' - tag.setAttribute(attr, val) - if (vueMetaAttrs.indexOf(attr) === -1) { - vueMetaAttrs.push(attr) - } - const saveIndex = toRemove.indexOf(attr) - if (saveIndex !== -1) { - toRemove.splice(saveIndex, 1) +export default function _updateTagAttributes ({ attribute }) { + /** + * updates the document's html tag attributes + * + * @param {Object} attrs - the new document html attributes + * @param {HTMLElement} tag - the HTMLElment tag to update with new attrs + */ + return function updateTagAttributes (attrs, tag) { + const vueMetaAttrString = tag.getAttribute(attribute) + const vueMetaAttrs = vueMetaAttrString ? vueMetaAttrString.split(',') : [] + const toRemove = [].concat(vueMetaAttrs) + for (let attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + const val = attrs[attr] || '' + tag.setAttribute(attr, val) + if (vueMetaAttrs.indexOf(attr) === -1) { + vueMetaAttrs.push(attr) + } + const saveIndex = toRemove.indexOf(attr) + if (saveIndex !== -1) { + toRemove.splice(saveIndex, 1) + } } } - } - let i = toRemove.length - 1 - for (; i >= 0; i--) { - tag.removeAttribute(toRemove[i]) - } - if (vueMetaAttrs.length === toRemove.length) { - tag.removeAttribute(VUE_META_ATTRIBUTE) - } else { - tag.setAttribute(VUE_META_ATTRIBUTE, vueMetaAttrs.join(',')) + let i = toRemove.length - 1 + for (; i >= 0; i--) { + tag.removeAttribute(toRemove[i]) + } + if (vueMetaAttrs.length === toRemove.length) { + tag.removeAttribute(attribute) + } else { + tag.setAttribute(attribute, vueMetaAttrs.join(',')) + } } } diff --git a/src/client/updaters/updateTags.js b/src/client/updaters/updateTags.js index 3db1dbd..82f6b45 100644 --- a/src/client/updaters/updateTags.js +++ b/src/client/updaters/updateTags.js @@ -1,59 +1,59 @@ -import { VUE_META_ATTRIBUTE } from '../../shared/constants' - // borrow the slice method const toArray = Function.prototype.call.bind(Array.prototype.slice) -/** - * Updates meta tags inside on the client. Borrowed from `react-helmet`: - * https://github.com/nfl/react-helmet/blob/004d448f8de5f823d10f838b02317521180f34da/src/Helmet.js#L195-L245 - * - * @param {('meta'|'base'|'link'|'style'|'script'|'noscript')} type - the name of the tag - * @param {(Array|Object)} tags - an array of tag objects or a single object in case of base - * @return {Object} - a representation of what tags changed - */ -export default function updateTags (type, tags, headTag) { - const nodes = headTag.querySelectorAll(`${type}[${VUE_META_ATTRIBUTE}]`) - const oldTags = toArray(nodes) - const newTags = [] - let indexToDelete +export default function _updateTags ({ attribute }) { + /** + * Updates meta tags inside on the client. Borrowed from `react-helmet`: + * https://github.com/nfl/react-helmet/blob/004d448f8de5f823d10f838b02317521180f34da/src/Helmet.js#L195-L245 + * + * @param {('meta'|'base'|'link'|'style'|'script'|'noscript')} type - the name of the tag + * @param {(Array|Object)} tags - an array of tag objects or a single object in case of base + * @return {Object} - a representation of what tags changed + */ + return function updateTags (type, tags, headTag) { + const nodes = headTag.querySelectorAll(`${type}[${attribute}]`) + const oldTags = toArray(nodes) + const newTags = [] + let indexToDelete - if (tags && tags.length) { - tags.forEach((tag) => { - const newElement = document.createElement(type) + if (tags && tags.length) { + tags.forEach((tag) => { + const newElement = document.createElement(type) - for (const attribute in tag) { - if (tag.hasOwnProperty(attribute)) { - if (attribute === 'innerHTML') { - newElement.innerHTML = tag.innerHTML - } else if (attribute === 'cssText') { - if (newElement.styleSheet) { - newElement.styleSheet.cssText = tag.cssText + for (const attr in tag) { + if (tag.hasOwnProperty(attr)) { + if (attribute === 'innerHTML') { + newElement.innerHTML = tag.innerHTML + } else if (attribute === 'cssText') { + if (newElement.styleSheet) { + newElement.styleSheet.cssText = tag.cssText + } else { + newElement.appendChild(document.createTextNode(tag.cssText)) + } } else { - newElement.appendChild(document.createTextNode(tag.cssText)) + const value = (typeof tag[attr] === 'undefined') ? '' : tag[attr] + newElement.setAttribute(attr, value) } - } else { - const value = (typeof tag[attribute] === 'undefined') ? '' : tag[attribute] - newElement.setAttribute(attribute, value) } } - } - newElement.setAttribute(VUE_META_ATTRIBUTE, 'true') + newElement.setAttribute(attribute, 'true') - // Remove a duplicate tag from domTagstoRemove, so it isn't cleared. - if (oldTags.some((existingTag, index) => { - indexToDelete = index - return newElement.isEqualNode(existingTag) - })) { - oldTags.splice(indexToDelete, 1) - } else { - newTags.push(newElement) - } - }) + // Remove a duplicate tag from domTagstoRemove, so it isn't cleared. + if (oldTags.some((existingTag, index) => { + indexToDelete = index + return newElement.isEqualNode(existingTag) + })) { + oldTags.splice(indexToDelete, 1) + } else { + newTags.push(newElement) + } + }) + } + + oldTags.forEach((tag) => tag.parentNode.removeChild(tag)) + newTags.forEach((tag) => headTag.appendChild(tag)) + + return { oldTags, newTags } } - - oldTags.forEach((tag) => tag.parentNode.removeChild(tag)) - newTags.forEach((tag) => headTag.appendChild(tag)) - - return { oldTags, newTags } } diff --git a/src/client/updaters/updateTitle.js b/src/client/updaters/updateTitle.js index 2797754..1787fbb 100644 --- a/src/client/updaters/updateTitle.js +++ b/src/client/updaters/updateTitle.js @@ -1,8 +1,10 @@ -/** - * updates the document title - * - * @param {String} title - the new title of the document - */ -export default function updateTitle (title = document.title) { - document.title = title +export default function _updateTitle () { + /** + * updates the document title + * + * @param {String} title - the new title of the document + */ + return function updateTitle (title = document.title) { + document.title = title + } } diff --git a/src/server/generateServerInjector.js b/src/server/generateServerInjector.js index 43f98dc..06d50f6 100644 --- a/src/server/generateServerInjector.js +++ b/src/server/generateServerInjector.js @@ -2,21 +2,23 @@ import titleGenerator from './generators/titleGenerator' import attrsGenerator from './generators/attrsGenerator' import tagGenerator from './generators/tagGenerator' -/** - * Converts a meta info property to one that can be stringified on the server - * - * @param {String} type - the type of data to convert - * @param {(String|Object|Array)} data - the data value - * @return {Object} - the new injector - */ -export default function generateServerInjector (type, data) { - switch (type) { - case 'title': - return titleGenerator(type, data) - case 'htmlAttrs': - case 'bodyAttrs': - return attrsGenerator(type, data) - default: - return tagGenerator(type, data) +export default function _generateServerInjector (options) { + /** + * Converts a meta info property to one that can be stringified on the server + * + * @param {String} type - the type of data to convert + * @param {(String|Object|Array)} data - the data value + * @return {Object} - the new injector + */ + return function generateServerInjector (type, data) { + switch (type) { + case 'title': + return titleGenerator(options)(type, data) + case 'htmlAttrs': + case 'bodyAttrs': + return attrsGenerator(options)(type, data) + default: + return tagGenerator(options)(type, data) + } } } diff --git a/src/server/generators/attrsGenerator.js b/src/server/generators/attrsGenerator.js index 6564e78..750a1ef 100644 --- a/src/server/generators/attrsGenerator.js +++ b/src/server/generators/attrsGenerator.js @@ -1,29 +1,29 @@ -import { VUE_META_ATTRIBUTE } from '../../shared/constants' - -/** - * Generates tag attributes for use on the server. - * - * @param {('bodyAttrs'|'htmlAttrs')} type - the type of attributes to generate - * @param {Object} data - the attributes to generate - * @return {Object} - the attribute generator - */ -export default function attrsGenerator (type, data) { - return { - text () { - let attributeStr = '' - let watchedAttrs = [] - for (let attr in data) { - if (data.hasOwnProperty(attr)) { - watchedAttrs.push(attr) - attributeStr += `${ - typeof data[attr] !== 'undefined' - ? `${attr}="${data[attr]}"` - : attr - } ` +export default function _attrsGenerator ({ attribute }) { + /** + * Generates tag attributes for use on the server. + * + * @param {('bodyAttrs'|'htmlAttrs')} type - the type of attributes to generate + * @param {Object} data - the attributes to generate + * @return {Object} - the attribute generator + */ + return function attrsGenerator (type, data) { + return { + text () { + let attributeStr = '' + let watchedAttrs = [] + for (let attr in data) { + if (data.hasOwnProperty(attr)) { + watchedAttrs.push(attr) + attributeStr += `${ + typeof data[attr] !== 'undefined' + ? `${attr}="${data[attr]}"` + : attr + } ` + } } + attributeStr += `${attribute}="${watchedAttrs.join(',')}"` + return attributeStr.trim() } - attributeStr += `${VUE_META_ATTRIBUTE}="${watchedAttrs.join(',')}"` - return attributeStr.trim() } } } diff --git a/src/server/generators/tagGenerator.js b/src/server/generators/tagGenerator.js index 4e6febd..4cff0cb 100644 --- a/src/server/generators/tagGenerator.js +++ b/src/server/generators/tagGenerator.js @@ -1,44 +1,44 @@ -import { VUE_META_ATTRIBUTE } from '../../shared/constants' +export default function _tagGenerator ({ attribute }) { + /** + * Generates meta, base, link, style, script, noscript tags for use on the server + * + * @param {('meta'|'base'|'link'|'style'|'script'|'noscript')} the name of the tag + * @param {(Array|Object)} tags - an array of tag objects or a single object in case of base + * @return {Object} - the tag generator + */ + return function tagGenerator (type, tags) { + return { + text () { + // build a string containing all tags of this type + return tags.reduce((tagsStr, tag) => { + // build a string containing all attributes of this tag + const attrs = Object.keys(tag).reduce((attrsStr, attr) => { + switch (attr) { + // these attributes are treated as children on the tag + case 'innerHTML': + case 'cssText': + return attrsStr -/** - * Generates meta, base, link, style, script, noscript tags for use on the server - * - * @param {('meta'|'base'|'link'|'style'|'script'|'noscript')} the name of the tag - * @param {(Array|Object)} tags - an array of tag objects or a single object in case of base - * @return {Object} - the tag generator - */ -export default function tagGenerator (type, tags) { - return { - text () { - // build a string containing all tags of this type - return tags.reduce((tagsStr, tag) => { - // build a string containing all attributes of this tag - const attrs = Object.keys(tag).reduce((attrsStr, attr) => { - switch (attr) { - // these attributes are treated as children on the tag - case 'innerHTML': - case 'cssText': - return attrsStr + // these form the attribute list for this tag + default: + return typeof tag[attr] === 'undefined' + ? `${attrsStr} ${attr}` + : `${attrsStr} ${attr}="${tag[attr]}"` + } + }, '').trim() - // these form the attribute list for this tag - default: - return typeof tag[attr] === 'undefined' - ? `${attrsStr} ${attr}` - : `${attrsStr} ${attr}="${tag[attr]}"` - } - }, '').trim() + // grab child content from one of these attributes, if possible + const content = tag.innerHTML || tag.cssText || '' - // grab child content from one of these attributes, if possible - const content = tag.innerHTML || tag.cssText || '' + // these tag types will have content inserted + const closed = ['noscript', 'script', 'style'].indexOf(type) === -1 - // these tag types will have content inserted - const closed = ['noscript', 'script', 'style'].indexOf(type) === -1 - - // the final string for this specific tag - return closed - ? `${tagsStr}<${type} ${VUE_META_ATTRIBUTE}="true" ${attrs}/>` - : `${tagsStr}<${type} ${VUE_META_ATTRIBUTE}="true" ${attrs}>${content}` - }, '') + // the final string for this specific tag + return closed + ? `${tagsStr}<${type} ${attribute}="true" ${attrs}/>` + : `${tagsStr}<${type} ${attribute}="true" ${attrs}>${content}` + }, '') + } } } } diff --git a/src/server/generators/titleGenerator.js b/src/server/generators/titleGenerator.js index 6886e9d..5f4c0a9 100644 --- a/src/server/generators/titleGenerator.js +++ b/src/server/generators/titleGenerator.js @@ -1,16 +1,16 @@ -import { VUE_META_ATTRIBUTE } from '../../shared/constants' - -/** - * Generates title output for the server - * - * @param {'title'} type - the string "title" - * @param {String} data - the title text - * @return {Object} - the title generator - */ -export default function titleGenerator (type, data) { - return { - text () { - return `<${type} ${VUE_META_ATTRIBUTE}="true">${data}` +export default function _titleGenerator ({ attribute }) { + /** + * Generates title output for the server + * + * @param {'title'} type - the string "title" + * @param {String} data - the title text + * @return {Object} - the title generator + */ + return function titleGenerator (type, data) { + return { + text () { + return `<${type} ${attribute}="true">${data}` + } } } } diff --git a/src/server/inject.js b/src/server/inject.js index bbbf108..1f30163 100644 --- a/src/server/inject.js +++ b/src/server/inject.js @@ -1,23 +1,25 @@ import getMetaInfo from '../shared/getMetaInfo' import generateServerInjector from './generateServerInjector' -/** - * Converts the state of the meta info object such that each item - * can be compiled to a tag string on the server - * - * @this {Object} - Vue instance - ideally the root component - * @return {Object} - server meta info with `toString` methods - */ -export default function inject () { - // get meta info with sensible defaults - const info = getMetaInfo(this.$root) +export default function _inject (options) { + /** + * Converts the state of the meta info object such that each item + * can be compiled to a tag string on the server + * + * @this {Object} - Vue instance - ideally the root component + * @return {Object} - server meta info with `toString` methods + */ + return function inject () { + // get meta info with sensible defaults + const info = getMetaInfo(options)(this.$root) - // generate server injectors - for (let key in info) { - if (info.hasOwnProperty(key) && key !== 'titleTemplate') { - info[key] = generateServerInjector(key, info[key]) + // generate server injectors + for (let key in info) { + if (info.hasOwnProperty(key) && key !== 'titleTemplate') { + info[key] = generateServerInjector(options)(key, info[key]) + } } - } - return info + return info + } } diff --git a/src/shared/$meta.js b/src/shared/$meta.js index e41e7a1..b8eaf03 100644 --- a/src/shared/$meta.js +++ b/src/shared/$meta.js @@ -1,15 +1,16 @@ import inject from '../server/inject' import refresh from '../client/refresh' -/** - * Returns an injector for server-side rendering. - * @this {Object} - the Vue instance (a root component) - * @return {Object} - injector - */ -export default function $meta () { - // bind inject method to this component - return { - inject: inject.bind(this), - refresh: refresh.bind(this) +export default function _$meta (options) { + /** + * Returns an injector for server-side rendering. + * @this {Object} - the Vue instance (a root component) + * @return {Object} - injector + */ + return function $meta () { + return { + inject: inject(options).bind(this), + refresh: refresh(options).bind(this) + } } } diff --git a/src/shared/constants.js b/src/shared/constants.js index 6ae5ace..7bd5c55 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -1,2 +1,21 @@ +/** + * These are constant variables used throughout the application. + */ + +// This is the name of the component option that contains all the information that +// gets converted to the various meta tags & attributes for the page. +export const VUE_META_KEY_NAME = 'metaInfo' + +// This is the attribute vue-meta augments on elements to know which it should +// manage and which it should ignore. export const VUE_META_ATTRIBUTE = 'data-vue-meta' -export const SERVER_RENDERED_ATTRIBUTE = 'data-vue-meta-server-rendered' + +// This is the attribute that goes on the `html` tag to inform `vue-meta` +// that the server has already generated the meta tags for the initial render. +export const VUE_META_SERVER_RENDERED_ATTRIBUTE = 'data-vue-meta-server-rendered' + +// This is the property that tells vue-meta to overwrite (instead of append) +// an item in a tag list. For example, if you have two `meta` tag list items +// that both have `vmid` of "description", then vue-meta will overwrite the +// shallowest one with the deepest one. +export const VUE_META_TAG_LIST_ID_KEY_NAME = 'vmid' diff --git a/src/shared/getMetaInfo.js b/src/shared/getMetaInfo.js index 3f7fd1a..2b38437 100644 --- a/src/shared/getMetaInfo.js +++ b/src/shared/getMetaInfo.js @@ -1,75 +1,77 @@ import deepmerge from 'deepmerge' import getComponentOption from './getComponentOption' -/** - * Returns the correct meta info for the given component - * (child components will overwrite parent meta info) - * - * @param {Object} component - the Vue instance to get meta info from - * @return {Object} - returned meta info - */ -export default function getMetaInfo (component) { - // set some sane defaults - const defaultInfo = { - title: '', - titleChunk: '', - titleTemplate: '%s', - htmlAttrs: {}, - bodyAttrs: {}, - meta: [], - base: [], - link: [], - style: [], - script: [], - noscript: [] - } +export default function _getMetaInfo ({ keyName, tagIDKeyName }) { + /** + * Returns the correct meta info for the given component + * (child components will overwrite parent meta info) + * + * @param {Object} component - the Vue instance to get meta info from + * @return {Object} - returned meta info + */ + return function getMetaInfo (component) { + // set some sane defaults + const defaultInfo = { + title: '', + titleChunk: '', + titleTemplate: '%s', + htmlAttrs: {}, + bodyAttrs: {}, + meta: [], + base: [], + link: [], + style: [], + script: [], + noscript: [] + } - // collect & aggregate all metaInfo $options - const info = getComponentOption({ - component, - option: 'metaInfo', - deep: true, - arrayMerge (target, source) { - // we concat the arrays without merging objects contained therein, - // but we check for a `vmid` property on each object in the array - // using an O(1) lookup associative array exploit - // note the use of "for in" - we are looping through arrays here, not - // plain objects - const destination = [] - for (let targetIndex in target) { - const targetItem = target[targetIndex] - let shared = false - for (let sourceIndex in source) { - const sourceItem = source[sourceIndex] - if (targetItem.vmid === sourceItem.vmid) { - shared = true - break + // collect & aggregate all metaInfo $options + const info = getComponentOption({ + component, + option: keyName, + deep: true, + arrayMerge (target, source) { + // we concat the arrays without merging objects contained therein, + // but we check for a `vmid` property on each object in the array + // using an O(1) lookup associative array exploit + // note the use of "for in" - we are looping through arrays here, not + // plain objects + const destination = [] + for (let targetIndex in target) { + const targetItem = target[targetIndex] + let shared = false + for (let sourceIndex in source) { + const sourceItem = source[sourceIndex] + if (targetItem[tagIDKeyName] === sourceItem[tagIDKeyName]) { + shared = true + break + } + } + if (!shared) { + destination.push(targetItem) } } - if (!shared) { - destination.push(targetItem) - } + + return destination.concat(source) } + }) - return destination.concat(source) + // backup the title chunk in case user wants access to it + if (info.title) { + info.titleChunk = info.title } - }) - // backup the title chunk in case user wants access to it - if (info.title) { - info.titleChunk = info.title + // replace title with populated template + if (info.titleTemplate) { + info.title = info.titleTemplate.replace(/%s/g, info.titleChunk) + } + + // convert base tag to an array so it can be handled the same way + // as the other tags + if (info.base) { + info.base = Object.keys(info.base).length ? [info.base] : [] + } + + return deepmerge(defaultInfo, info) } - - // replace title with populated template - if (info.titleTemplate) { - info.title = info.titleTemplate.replace(/%s/g, info.titleChunk) - } - - // convert base tag to an array so it can be handled the same way - // as the other tags - if (info.base) { - info.base = Object.keys(info.base).length ? [info.base] : [] - } - - return deepmerge(defaultInfo, info) } diff --git a/src/shared/plugin.js b/src/shared/plugin.js index e333c19..6f97512 100644 --- a/src/shared/plugin.js +++ b/src/shared/plugin.js @@ -1,17 +1,36 @@ +import assign from 'object-assign' import $meta from './$meta' +import { + VUE_META_KEY_NAME, + VUE_META_ATTRIBUTE, + VUE_META_SERVER_RENDERED_ATTRIBUTE, + VUE_META_TAG_LIST_ID_KEY_NAME +} from './constants' + // automatic install if (typeof Vue !== 'undefined') { Vue.use(VueMeta) } +// set some default options +const defaultOptions = { + keyName: VUE_META_KEY_NAME, + attribute: VUE_META_ATTRIBUTE, + ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE, + tagIDKeyName: VUE_META_TAG_LIST_ID_KEY_NAME +} + /** * Plugin install function. * @param {Function} Vue - the Vue constructor. */ -export default function VueMeta (Vue) { +export default function VueMeta (Vue, options = {}) { + // combine options + options = assign(defaultOptions, options) + // bind the $meta method to this component instance - Vue.prototype.$meta = $meta + Vue.prototype.$meta = $meta(options) // store an id to keep track of DOM updates let requestId = null