mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-09 01:52:25 +03:00
feat: better template support
This commit is contained in:
Generated
+4463
-3947
File diff suppressed because it is too large
Load Diff
@@ -22,3 +22,6 @@ export const VUE_META_TAG_LIST_ID_KEY_NAME = 'vmid'
|
||||
|
||||
// This is the key name for possible meta templates
|
||||
export const VUE_META_TEMPLATE_KEY_NAME = 'template'
|
||||
|
||||
// This is the key name for the content-holding property
|
||||
export const VUE_META_CONTENT_KEY = 'content'
|
||||
|
||||
@@ -15,7 +15,7 @@ import deepmerge from 'deepmerge'
|
||||
* @return {Object} result - final aggregated result
|
||||
*/
|
||||
export default function getComponentOption (opts, result = {}) {
|
||||
const { component, option, deep, arrayMerge, metaTemplateKeyName } = opts
|
||||
const { component, option, deep, arrayMerge, metaTemplateKeyName, contentKeyName } = opts
|
||||
const { $options } = component
|
||||
|
||||
if (component._inactive) return result
|
||||
@@ -51,7 +51,7 @@ export default function getComponentOption (opts, result = {}) {
|
||||
if (metaTemplateKeyName && result.hasOwnProperty('meta')) {
|
||||
result.meta = Object.keys(result.meta).map(metaKey => {
|
||||
const metaObject = result.meta[metaKey]
|
||||
if (!metaObject.hasOwnProperty(metaTemplateKeyName) || !metaObject.hasOwnProperty('content') || typeof metaObject[metaTemplateKeyName] === 'undefined') {
|
||||
if (!metaObject.hasOwnProperty(metaTemplateKeyName) || !metaObject.hasOwnProperty(contentKeyName) || typeof metaObject[metaTemplateKeyName] === 'undefined') {
|
||||
return result.meta[metaKey]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const escapeHTML = (str) => typeof window === 'undefined'
|
||||
.replace(/'/g, '\u0027')
|
||||
|
||||
export default function _getMetaInfo (options = {}) {
|
||||
const { keyName, tagIDKeyName, metaTemplateKeyName } = options
|
||||
const { keyName, tagIDKeyName, metaTemplateKeyName, contentKeyName } = options
|
||||
/**
|
||||
* Returns the correct meta info for the given component
|
||||
* (child components will overwrite parent meta info)
|
||||
@@ -53,6 +53,7 @@ export default function _getMetaInfo (options = {}) {
|
||||
option: keyName,
|
||||
deep: true,
|
||||
metaTemplateKeyName,
|
||||
contentKeyName,
|
||||
arrayMerge (target, source) {
|
||||
// we concat the arrays without merging objects contained in,
|
||||
// but we check for a `vmid` property on each object in the array
|
||||
@@ -66,10 +67,17 @@ export default function _getMetaInfo (options = {}) {
|
||||
for (let sourceIndex in source) {
|
||||
const sourceItem = source[sourceIndex]
|
||||
if (targetItem[tagIDKeyName] && targetItem[tagIDKeyName] === sourceItem[tagIDKeyName]) {
|
||||
const targetTemplate = targetItem[metaTemplateKeyName]
|
||||
const sourceTemplate = sourceItem[metaTemplateKeyName]
|
||||
if (targetTemplate && !sourceTemplate) {
|
||||
sourceItem[contentKeyName] = applyTemplate(component)(targetTemplate)(sourceItem[contentKeyName])
|
||||
delete sourceItem[metaTemplateKeyName]
|
||||
}
|
||||
shared = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!shared) {
|
||||
destination.push(targetItem)
|
||||
}
|
||||
@@ -79,6 +87,8 @@ export default function _getMetaInfo (options = {}) {
|
||||
}
|
||||
})
|
||||
|
||||
// Remove all "template" tags from meta
|
||||
|
||||
// backup the title chunk in case user wants access to it
|
||||
if (info.title) {
|
||||
info.titleChunk = info.title
|
||||
@@ -86,11 +96,7 @@ export default function _getMetaInfo (options = {}) {
|
||||
|
||||
// replace title with populated template
|
||||
if (info.titleTemplate) {
|
||||
if (typeof info.titleTemplate === 'function') {
|
||||
info.title = info.titleTemplate.call(component, info.titleChunk)
|
||||
} else {
|
||||
info.title = info.titleTemplate.replace(/%s/g, info.titleChunk)
|
||||
}
|
||||
info.title = applyTemplate(component)(info.titleTemplate)(info.titleChunk)
|
||||
}
|
||||
|
||||
// convert base tag to an array so it can be handled the same way
|
||||
@@ -140,3 +146,6 @@ export default function _getMetaInfo (options = {}) {
|
||||
return info
|
||||
}
|
||||
}
|
||||
|
||||
const applyTemplate = component => template => chunk =>
|
||||
typeof template === 'function' ? template.call(component, chunk) : template.replace(/%s/g, chunk)
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
VUE_META_ATTRIBUTE,
|
||||
VUE_META_SERVER_RENDERED_ATTRIBUTE,
|
||||
VUE_META_TAG_LIST_ID_KEY_NAME,
|
||||
VUE_META_TEMPLATE_KEY_NAME
|
||||
VUE_META_TEMPLATE_KEY_NAME, VUE_META_CONTENT_KEY
|
||||
} from './constants'
|
||||
|
||||
// automatic install
|
||||
@@ -23,6 +23,7 @@ export default function VueMeta (Vue, options = {}) {
|
||||
// set some default options
|
||||
const defaultOptions = {
|
||||
keyName: VUE_META_KEY_NAME,
|
||||
contentKeyName: VUE_META_CONTENT_KEY,
|
||||
metaTemplateKeyName: VUE_META_TEMPLATE_KEY_NAME,
|
||||
attribute: VUE_META_ATTRIBUTE,
|
||||
ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE,
|
||||
|
||||
@@ -2,6 +2,7 @@ import Vue from 'vue'
|
||||
import _getMetaInfo from '../src/shared/getMetaInfo'
|
||||
import {
|
||||
VUE_META_ATTRIBUTE,
|
||||
VUE_META_CONTENT_KEY,
|
||||
VUE_META_KEY_NAME,
|
||||
VUE_META_SERVER_RENDERED_ATTRIBUTE,
|
||||
VUE_META_TAG_LIST_ID_KEY_NAME,
|
||||
@@ -14,6 +15,7 @@ const defaultOptions = {
|
||||
attribute: VUE_META_ATTRIBUTE,
|
||||
ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE,
|
||||
metaTemplateKeyName: VUE_META_TEMPLATE_KEY_NAME,
|
||||
contentKeyName: VUE_META_CONTENT_KEY,
|
||||
tagIDKeyName: VUE_META_TAG_LIST_ID_KEY_NAME
|
||||
}
|
||||
|
||||
@@ -363,4 +365,169 @@ describe('getMetaInfo', () => {
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
it('properly uses meta templates with one-level-deep nested children content', () => {
|
||||
Vue.component('merge-child', {
|
||||
template: '<div></div>',
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title!'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
component = new Vue({
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
},
|
||||
render: (h) => h('div', null, [h('merge-child')]),
|
||||
el: document.createElement('div')
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).to.eql({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title! - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: Still failing :( Child template won't be applied if child has no content as well
|
||||
|
||||
it('properly uses meta templates with one-level-deep nested children template', () => {
|
||||
Vue.component('merge-child', {
|
||||
template: '<div></div>',
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
component = new Vue({
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - SHOULD NEVER HAPPEN`
|
||||
}
|
||||
]
|
||||
},
|
||||
render: (h) => h('div', null, [h('merge-child')]),
|
||||
el: document.createElement('div')
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).to.eql({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
it('properly uses meta templates with one-level-deep nested children template and content', () => {
|
||||
Vue.component('merge-child', {
|
||||
template: '<div></div>',
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title!',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
component = new Vue({
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - SHOULD NEVER HAPPEN`
|
||||
}
|
||||
]
|
||||
},
|
||||
render: (h) => h('div', null, [h('merge-child')]),
|
||||
el: document.createElement('div')
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).to.eql({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title! - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user