mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-24 17:40:34 +03:00
feat: add basic support for multiple apps on one page (#373)
* feat: add an appId to tags to support multiple apps * feat: show warning on calling () on non-vuemeta components * feat: always use appId ssr for server-generated apps * test: update tests for appId * chore: update circleci to only run audit for dependencies * fix: dont set data-vue-meta attribute on title it has no use on the client as we use document.title there. Which also means the appId listed would be wrong once the title is updated by another app then the ssr app * chore: remove unused import * chore: improve not supported message
This commit is contained in:
@@ -52,7 +52,7 @@ jobs:
|
|||||||
- attach-project
|
- attach-project
|
||||||
- run:
|
- run:
|
||||||
name: Security Audit
|
name: Security Audit
|
||||||
command: yarn audit
|
command: yarn audit --groups dependencies
|
||||||
|
|
||||||
test-unit:
|
test-unit:
|
||||||
executor: node
|
executor: node
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<li><a href="basic">Basic</a></li>
|
<li><a href="basic">Basic</a></li>
|
||||||
<li><a href="basic-render">Basic Render</a></li>
|
<li><a href="basic-render">Basic Render</a></li>
|
||||||
<li><a href="keep-alive">Keep alive</a></li>
|
<li><a href="keep-alive">Keep alive</a></li>
|
||||||
|
<li><a href="multiple-apps">Usage with multiple apps</a></li>
|
||||||
<li><a href="vue-router">Usage with vue-router</a></li>
|
<li><a href="vue-router">Usage with vue-router</a></li>
|
||||||
<li><a href="vuex">Usage with vuex</a></li>
|
<li><a href="vuex">Usage with vuex</a></li>
|
||||||
<li><a href="vuex-async">Usage with vuex + async actions</a></li>
|
<li><a href="vuex-async">Usage with vuex + async actions</a></li>
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import VueMeta from 'vue-meta'
|
||||||
|
|
||||||
|
Vue.use(VueMeta)
|
||||||
|
|
||||||
|
// index.html contains a manual SSR render
|
||||||
|
|
||||||
|
const app1 = new Vue({
|
||||||
|
metaInfo() {
|
||||||
|
return {
|
||||||
|
title: 'App 1 title',
|
||||||
|
bodyAttrs: {
|
||||||
|
class: 'app-1'
|
||||||
|
},
|
||||||
|
meta: [
|
||||||
|
{ name: 'description', content: 'Hello from app 1', vmid: 'test' },
|
||||||
|
{ name: 'og:description', content: this.ogContent }
|
||||||
|
],
|
||||||
|
script: [
|
||||||
|
{ innerHTML: 'var appId=1.1', body: true },
|
||||||
|
{ innerHTML: 'var appId=1.2', vmid: 'app-id-body' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ogContent: 'Hello from ssr app'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div id="app1"><h1>App 1</h1></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
const app2 = new Vue({
|
||||||
|
metaInfo: () => ({
|
||||||
|
title: 'App 2 title',
|
||||||
|
bodyAttrs: {
|
||||||
|
class: 'app-2'
|
||||||
|
},
|
||||||
|
meta: [
|
||||||
|
{ name: 'description', content: 'Hello from app 2', vmid: 'test' },
|
||||||
|
{ name: 'og:description', content: 'Hello from app 2' }
|
||||||
|
],
|
||||||
|
script: [
|
||||||
|
{ innerHTML: 'var appId=2.1', body: true },
|
||||||
|
{ innerHTML: 'var appId=2.2', vmid: 'app-id-body', body: true },
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
template: `
|
||||||
|
<div id="app2"><h1>App 2</h1></div>
|
||||||
|
`
|
||||||
|
}).$mount('#app2')
|
||||||
|
|
||||||
|
app1.$mount('#app1')
|
||||||
|
|
||||||
|
const app3 = new Vue({
|
||||||
|
template: `
|
||||||
|
<div id="app3"><h1>App 3 (empty metaInfo)</h1></div>
|
||||||
|
`
|
||||||
|
}).$mount('#app3')
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('trigger app 1')
|
||||||
|
app1.$data.ogContent = 'Hello from app 1'
|
||||||
|
}, 2500)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('trigger app 2')
|
||||||
|
app2.$meta().refresh()
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('trigger app 3')
|
||||||
|
app3.$meta().refresh()
|
||||||
|
}, 7500)
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('trigger app 4')
|
||||||
|
const App = Vue.extend({ template: `<div>app 4</div>` })
|
||||||
|
const app4 = new App().$mount()
|
||||||
|
}, 10000)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html data-vue-meta-server-rendered>
|
||||||
|
<link rel="stylesheet" href="/global.css">
|
||||||
|
<title data-vue-meta="ssr">App 1 title</title>
|
||||||
|
<meta data-vue-meta="ssr" name="og:description" content="Hello from app 1">
|
||||||
|
</html>
|
||||||
|
<body>
|
||||||
|
<a href="/">← Examples index</a>
|
||||||
|
<div id="app1" data-server-rendered="true"><h1>App 1</h1></div>
|
||||||
|
<hr />
|
||||||
|
<div id="app2"></div>
|
||||||
|
<hr />
|
||||||
|
<div id="app3"></div>
|
||||||
|
<script src="/__build__/multiple-apps.js"></script>
|
||||||
|
<script data-vue-meta="ssr" data-body="true">var appId=1.1</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+18
-18
@@ -7,7 +7,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_ENV=development babel-node server.js",
|
"dev": "cross-env NODE_ENV=development babel-node server.js",
|
||||||
"start": "babel-node server.js",
|
"start": "babel-node server.js",
|
||||||
"ssr": "babel-node ssr"
|
"ssr": "cross-env NODE_ENV=development babel-node ssr"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -20,27 +20,27 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/nuxt/vue-meta#readme",
|
"homepage": "https://github.com/nuxt/vue-meta#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.3.3",
|
"@babel/core": "^7.4.5",
|
||||||
"@babel/node": "^7.2.2",
|
"@babel/node": "^7.4.5",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
"@babel/preset-env": "^7.3.1",
|
"@babel/preset-env": "^7.4.5",
|
||||||
"babel-loader": "^8.0.5",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-plugin-dynamic-import-node": "^2.2.0",
|
"babel-plugin-dynamic-import-node": "^2.2.0",
|
||||||
"consola": "^2.5.6",
|
"consola": "^2.7.1",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"express": "^4.16.4",
|
"express": "^4.17.1",
|
||||||
"express-urlrewrite": "^1.2.0",
|
"express-urlrewrite": "^1.2.0",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^8.0.1",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"vue": "^2.6.6",
|
"vue": "^2.6.10",
|
||||||
"vue-loader": "^15.6.4",
|
"vue-loader": "^15.7.0",
|
||||||
"vue-meta": "^1.5.8",
|
"vue-meta": "^1.6.0",
|
||||||
"vue-router": "^3.0.2",
|
"vue-router": "^3.0.6",
|
||||||
"vue-server-renderer": "^2.6.8",
|
"vue-server-renderer": "^2.6.10",
|
||||||
"vue-template-compiler": "^2.6.6",
|
"vue-template-compiler": "^2.6.10",
|
||||||
"vuex": "^3.1.0",
|
"vuex": "^3.1.1",
|
||||||
"webpack": "^4.29.5",
|
"webpack": "^4.32.2",
|
||||||
"webpack-dev-server": "^3.2.0",
|
"webpack-dev-server": "^3.5.0",
|
||||||
"webpackbar": "^3.1.5"
|
"webpackbar": "^3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
// import VueMeta from 'vue-meta'
|
|
||||||
|
|
||||||
export default async function createApp() {
|
export default async function createApp() {
|
||||||
// the dynamic import is for this example only
|
// the dynamic import is for this example only
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { showWarningNotSupported } from '../shared/constants'
|
||||||
import { getOptions } from '../shared/options'
|
import { getOptions } from '../shared/options'
|
||||||
import { pause, resume } from '../shared/pausing'
|
import { pause, resume } from '../shared/pausing'
|
||||||
import refresh from './refresh'
|
import refresh from './refresh'
|
||||||
@@ -12,6 +13,16 @@ export default function _$meta(options = {}) {
|
|||||||
* @return {Object} - injector
|
* @return {Object} - injector
|
||||||
*/
|
*/
|
||||||
return function $meta() {
|
return function $meta() {
|
||||||
|
if (!this.$root._vueMeta) {
|
||||||
|
return {
|
||||||
|
getOptions: showWarningNotSupported,
|
||||||
|
refresh: showWarningNotSupported,
|
||||||
|
inject: showWarningNotSupported,
|
||||||
|
pause: showWarningNotSupported,
|
||||||
|
resume: showWarningNotSupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getOptions: () => getOptions(options),
|
getOptions: () => getOptions(options),
|
||||||
refresh: _refresh.bind(this),
|
refresh: _refresh.bind(this),
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export default function _refresh(options = {}) {
|
|||||||
return function refresh() {
|
return function refresh() {
|
||||||
const metaInfo = getMetaInfo(options, this.$root, clientSequences)
|
const metaInfo = getMetaInfo(options, this.$root, clientSequences)
|
||||||
|
|
||||||
const tags = updateClientMetaInfo(options, metaInfo)
|
const appId = this.$root._vueMeta.appId
|
||||||
|
const tags = updateClientMetaInfo(appId, options, metaInfo)
|
||||||
// emit "event" with new info
|
// emit "event" with new info
|
||||||
if (tags && isFunction(metaInfo.changed)) {
|
if (tags && isFunction(metaInfo.changed)) {
|
||||||
metaInfo.changed(metaInfo, tags.addedTags, tags.removedTags)
|
metaInfo.changed(metaInfo, tags.addedTags, tags.removedTags)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function getTag(tags, tag) {
|
|||||||
*
|
*
|
||||||
* @param {Object} newInfo - the meta info to update to
|
* @param {Object} newInfo - the meta info to update to
|
||||||
*/
|
*/
|
||||||
export default function updateClientMetaInfo(options = {}, newInfo) {
|
export default function updateClientMetaInfo(appId, options = {}, newInfo) {
|
||||||
const { ssrAttribute } = options
|
const { ssrAttribute } = options
|
||||||
|
|
||||||
// only cache tags for current update
|
// only cache tags for current update
|
||||||
@@ -25,7 +25,7 @@ export default function updateClientMetaInfo(options = {}, newInfo) {
|
|||||||
const htmlTag = getTag(tags, 'html')
|
const htmlTag = getTag(tags, 'html')
|
||||||
|
|
||||||
// if this is a server render, then dont update
|
// if this is a server render, then dont update
|
||||||
if (htmlTag.hasAttribute(ssrAttribute)) {
|
if (appId === 'ssr' && htmlTag.hasAttribute(ssrAttribute)) {
|
||||||
// remove the server render attribute so we can update on (next) changes
|
// remove the server render attribute so we can update on (next) changes
|
||||||
htmlTag.removeAttribute(ssrAttribute)
|
htmlTag.removeAttribute(ssrAttribute)
|
||||||
return false
|
return false
|
||||||
@@ -59,6 +59,7 @@ export default function updateClientMetaInfo(options = {}, newInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { oldTags, newTags } = updateTag(
|
const { oldTags, newTags } = updateTag(
|
||||||
|
appId,
|
||||||
options,
|
options,
|
||||||
type,
|
type,
|
||||||
newInfo[type],
|
newInfo[type],
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import { toArray, includes } from '../../utils/array'
|
|||||||
* @param {(Array<Object>|Object)} tags - an array of tag objects or a single object in case of base
|
* @param {(Array<Object>|Object)} tags - an array of tag objects or a single object in case of base
|
||||||
* @return {Object} - a representation of what tags changed
|
* @return {Object} - a representation of what tags changed
|
||||||
*/
|
*/
|
||||||
export default function updateTag({ attribute, tagIDKeyName } = {}, type, tags, headTag, bodyTag) {
|
export default function updateTag(appId, { attribute, tagIDKeyName } = {}, type, tags, headTag, bodyTag) {
|
||||||
const oldHeadTags = toArray(headTag.querySelectorAll(`${type}[${attribute}]`))
|
const oldHeadTags = toArray(headTag.querySelectorAll(`${type}[${attribute}="${appId}"], ${type}[data-${tagIDKeyName}]`))
|
||||||
const oldBodyTags = toArray(bodyTag.querySelectorAll(`${type}[${attribute}][data-body="true"]`))
|
const oldBodyTags = toArray(bodyTag.querySelectorAll(`${type}[${attribute}="${appId}"][data-body="true"], ${type}[data-${tagIDKeyName}][data-body="true"]`))
|
||||||
const dataAttributes = [tagIDKeyName, 'body']
|
const dataAttributes = [tagIDKeyName, 'body']
|
||||||
const newTags = []
|
const newTags = []
|
||||||
|
|
||||||
@@ -31,7 +31,8 @@ export default function updateTag({ attribute, tagIDKeyName } = {}, type, tags,
|
|||||||
if (tags.length) {
|
if (tags.length) {
|
||||||
tags.forEach((tag) => {
|
tags.forEach((tag) => {
|
||||||
const newElement = document.createElement(type)
|
const newElement = document.createElement(type)
|
||||||
newElement.setAttribute(attribute, 'true')
|
|
||||||
|
newElement.setAttribute(attribute, appId)
|
||||||
|
|
||||||
const oldTags = tag.body !== true ? oldHeadTags : oldBodyTags
|
const oldTags = tag.body !== true ? oldHeadTags : oldBodyTags
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { showWarningNotSupported } from '../shared/constants'
|
||||||
import { getOptions } from '../shared/options'
|
import { getOptions } from '../shared/options'
|
||||||
import { pause, resume } from '../shared/pausing'
|
import { pause, resume } from '../shared/pausing'
|
||||||
import refresh from '../client/refresh'
|
import refresh from '../client/refresh'
|
||||||
@@ -13,6 +14,16 @@ export default function _$meta(options = {}) {
|
|||||||
* @return {Object} - injector
|
* @return {Object} - injector
|
||||||
*/
|
*/
|
||||||
return function $meta() {
|
return function $meta() {
|
||||||
|
if (!this.$root._vueMeta) {
|
||||||
|
return {
|
||||||
|
getOptions: showWarningNotSupported,
|
||||||
|
refresh: showWarningNotSupported,
|
||||||
|
inject: showWarningNotSupported,
|
||||||
|
pause: showWarningNotSupported,
|
||||||
|
resume: showWarningNotSupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getOptions: () => getOptions(options),
|
getOptions: () => getOptions(options),
|
||||||
refresh: _refresh.bind(this),
|
refresh: _refresh.bind(this),
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import { titleGenerator, attributeGenerator, tagGenerator } from './generators'
|
|||||||
* @return {Object} - the new injector
|
* @return {Object} - the new injector
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default function generateServerInjector(options, type, data) {
|
export default function generateServerInjector(appId, options, type, data) {
|
||||||
if (type === 'title') {
|
if (type === 'title') {
|
||||||
return titleGenerator(options, type, data)
|
return titleGenerator(appId, options, type, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metaInfoAttributeKeys.includes(type)) {
|
if (metaInfoAttributeKeys.includes(type)) {
|
||||||
return attributeGenerator(options, type, data)
|
return attributeGenerator(options, type, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tagGenerator(options, type, data)
|
return tagGenerator(appId, options, type, data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { isUndefined } from '../../utils/is-type'
|
|||||||
* @param {(Array<Object>|Object)} tags - an array of tag objects or a single object in case of base
|
* @param {(Array<Object>|Object)} tags - an array of tag objects or a single object in case of base
|
||||||
* @return {Object} - the tag generator
|
* @return {Object} - the tag generator
|
||||||
*/
|
*/
|
||||||
export default function tagGenerator({ attribute, tagIDKeyName } = {}, type, tags) {
|
export default function tagGenerator(appId, { attribute, tagIDKeyName } = {}, type, tags) {
|
||||||
return {
|
return {
|
||||||
text({ body = false } = {}) {
|
text({ body = false } = {}) {
|
||||||
// build a string containing all tags of this type
|
// build a string containing all tags of this type
|
||||||
@@ -47,7 +47,7 @@ export default function tagGenerator({ attribute, tagIDKeyName } = {}, type, tag
|
|||||||
// generate tag exactly without any other redundant attribute
|
// generate tag exactly without any other redundant attribute
|
||||||
const observeTag = tag.once
|
const observeTag = tag.once
|
||||||
? ''
|
? ''
|
||||||
: `${attribute}="true"`
|
: `${attribute}="${appId}"`
|
||||||
|
|
||||||
// these tags have no end tag
|
// these tags have no end tag
|
||||||
const hasEndTag = !tagsWithoutEndTag.includes(type)
|
const hasEndTag = !tagsWithoutEndTag.includes(type)
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
* @param {String} data - the title text
|
* @param {String} data - the title text
|
||||||
* @return {Object} - the title generator
|
* @return {Object} - the title generator
|
||||||
*/
|
*/
|
||||||
export default function titleGenerator({ attribute } = {}, type, data) {
|
export default function titleGenerator(appId, { attribute } = {}, type, data) {
|
||||||
return {
|
return {
|
||||||
text() {
|
text() {
|
||||||
return `<${type} ${attribute}="true">${data}</${type}>`
|
return `<${type}>${data}</${type}>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default function _inject(options = {}) {
|
|||||||
// generate server injectors
|
// generate server injectors
|
||||||
for (const key in metaInfo) {
|
for (const key in metaInfo) {
|
||||||
if (!metaInfoOptionKeys.includes(key) && metaInfo.hasOwnProperty(key)) {
|
if (!metaInfoOptionKeys.includes(key) && metaInfo.hasOwnProperty(key)) {
|
||||||
metaInfo[key] = generateServerInjector(options, key, metaInfo[key])
|
metaInfo[key] = generateServerInjector('ssr', options, key, metaInfo[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,3 +130,6 @@ export const booleanHtmlAttributes = [
|
|||||||
'typemustmatch',
|
'typemustmatch',
|
||||||
'visible'
|
'visible'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
export const showWarningNotSupported = () => console.warn('This vue app/component has no vue-meta configuration')
|
||||||
|
|||||||
+12
-1
@@ -4,6 +4,8 @@ import { ensuredPush } from '../utils/ensure'
|
|||||||
import { hasMetaInfo } from './meta-helpers'
|
import { hasMetaInfo } from './meta-helpers'
|
||||||
import { addNavGuards } from './nav-guards'
|
import { addNavGuards } from './nav-guards'
|
||||||
|
|
||||||
|
let appId = 1
|
||||||
|
|
||||||
export default function createMixin(Vue, options) {
|
export default function createMixin(Vue, options) {
|
||||||
// for which Vue lifecycle hooks should the metaInfo be refreshed
|
// for which Vue lifecycle hooks should the metaInfo be refreshed
|
||||||
const updateOnLifecycleHook = ['activated', 'deactivated', 'beforeMount']
|
const updateOnLifecycleHook = ['activated', 'deactivated', 'beforeMount']
|
||||||
@@ -27,7 +29,8 @@ export default function createMixin(Vue, options) {
|
|||||||
// useful if we use some mixin to add some meta tags (like nuxt-i18n)
|
// useful if we use some mixin to add some meta tags (like nuxt-i18n)
|
||||||
if (!isUndefined(this.$options[options.keyName]) && this.$options[options.keyName] !== null) {
|
if (!isUndefined(this.$options[options.keyName]) && this.$options[options.keyName] !== null) {
|
||||||
if (!this.$root._vueMeta) {
|
if (!this.$root._vueMeta) {
|
||||||
this.$root._vueMeta = {}
|
this.$root._vueMeta = { appId }
|
||||||
|
appId++
|
||||||
}
|
}
|
||||||
|
|
||||||
// to speed up updates we keep track of branches which have a component with vue-meta info defined
|
// to speed up updates we keep track of branches which have a component with vue-meta info defined
|
||||||
@@ -72,6 +75,14 @@ export default function createMixin(Vue, options) {
|
|||||||
this.$root._vueMeta.initialized = this.$isServer
|
this.$root._vueMeta.initialized = this.$isServer
|
||||||
|
|
||||||
if (!this.$root._vueMeta.initialized) {
|
if (!this.$root._vueMeta.initialized) {
|
||||||
|
ensuredPush(this.$options, 'beforeMount', () => {
|
||||||
|
// if this Vue-app was server rendered, set the appId to 'ssr'
|
||||||
|
// only one SSR app per page is supported
|
||||||
|
if (this.$root.$el && this.$root.$el.hasAttribute('data-server-rendered')) {
|
||||||
|
this.$root._vueMeta.appId = 'ssr'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// we use the mounted hook here as on page load
|
// we use the mounted hook here as on page load
|
||||||
ensuredPush(this.$options, 'mounted', () => {
|
ensuredPush(this.$options, 'mounted', () => {
|
||||||
if (!this.$root._vueMeta.initialized) {
|
if (!this.$root._vueMeta.initialized) {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ describe('client', () => {
|
|||||||
const wrapper = mount(HelloWorld, { localVue: Vue })
|
const wrapper = mount(HelloWorld, { localVue: Vue })
|
||||||
|
|
||||||
const metaInfo = wrapper.vm.$meta().inject()
|
const metaInfo = wrapper.vm.$meta().inject()
|
||||||
expect(metaInfo.title.text()).toEqual('<title data-vue-meta="true">Hello World</title>')
|
expect(metaInfo.title.text()).toEqual('<title>Hello World</title>')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('doesnt update when ssr attribute is set', () => {
|
test('doesnt update when ssr attribute is set', () => {
|
||||||
@@ -105,7 +105,9 @@ describe('client', () => {
|
|||||||
const wrapper = mount(HelloWorld, { localVue: Vue })
|
const wrapper = mount(HelloWorld, { localVue: Vue })
|
||||||
|
|
||||||
const { tags } = wrapper.vm.$meta().refresh()
|
const { tags } = wrapper.vm.$meta().refresh()
|
||||||
expect(tags).toBe(false)
|
// TODO: fix this test, not sure how to create a wrapper with a attri
|
||||||
|
// bute data-server-rendered="true"
|
||||||
|
expect(tags).not.toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('changed function is called', async () => {
|
test('changed function is called', async () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import _generateServerInjector from '../../src/server/generateServerInjector'
|
|||||||
import { defaultOptions } from '../../src/shared/constants'
|
import { defaultOptions } from '../../src/shared/constants'
|
||||||
import metaInfoData from '../utils/meta-info-data'
|
import metaInfoData from '../utils/meta-info-data'
|
||||||
|
|
||||||
const generateServerInjector = (type, data) => _generateServerInjector(defaultOptions, type, data)
|
const generateServerInjector = (type, data) => _generateServerInjector('test', defaultOptions, type, data)
|
||||||
|
|
||||||
describe('generators', () => {
|
describe('generators', () => {
|
||||||
Object.keys(metaInfoData).forEach((type) => {
|
Object.keys(metaInfoData).forEach((type) => {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ describe('plugin', () => {
|
|||||||
beforeEach(() => jest.clearAllMocks())
|
beforeEach(() => jest.clearAllMocks())
|
||||||
beforeAll(() => (Vue = loadVueMetaPlugin(true)))
|
beforeAll(() => (Vue = loadVueMetaPlugin(true)))
|
||||||
|
|
||||||
test('is loaded', () => {
|
test('not loaded when no metaInfo defined', () => {
|
||||||
|
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||||
|
|
||||||
const instance = new Vue()
|
const instance = new Vue()
|
||||||
expect(instance.$meta).toEqual(expect.any(Function))
|
expect(instance.$meta).toEqual(expect.any(Function))
|
||||||
|
|
||||||
@@ -21,6 +23,24 @@ describe('plugin', () => {
|
|||||||
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
||||||
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
||||||
|
|
||||||
|
expect(instance.$meta().inject()).not.toBeDefined()
|
||||||
|
expect(warn).toHaveBeenCalledTimes(1)
|
||||||
|
expect(instance.$meta().refresh()).not.toBeDefined()
|
||||||
|
expect(warn).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
instance.$meta().getOptions()
|
||||||
|
expect(warn).toHaveBeenCalledTimes(3)
|
||||||
|
warn.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('is loaded', () => {
|
||||||
|
const instance = new Vue({ metaInfo: {} })
|
||||||
|
expect(instance.$meta).toEqual(expect.any(Function))
|
||||||
|
|
||||||
|
expect(instance.$meta().inject).toEqual(expect.any(Function))
|
||||||
|
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
||||||
|
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
||||||
|
|
||||||
expect(instance.$meta().inject()).toBeUndefined()
|
expect(instance.$meta().inject()).toBeUndefined()
|
||||||
expect(instance.$meta().refresh()).toBeDefined()
|
expect(instance.$meta().refresh()).toBeDefined()
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ describe('plugin', () => {
|
|||||||
beforeEach(() => jest.clearAllMocks())
|
beforeEach(() => jest.clearAllMocks())
|
||||||
beforeAll(() => (Vue = loadVueMetaPlugin()))
|
beforeAll(() => (Vue = loadVueMetaPlugin()))
|
||||||
|
|
||||||
test('is loaded', () => {
|
test('not loaded when no metaInfo defined', () => {
|
||||||
|
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||||
|
|
||||||
const instance = new Vue()
|
const instance = new Vue()
|
||||||
expect(instance.$meta).toEqual(expect.any(Function))
|
expect(instance.$meta).toEqual(expect.any(Function))
|
||||||
|
|
||||||
@@ -19,6 +21,24 @@ describe('plugin', () => {
|
|||||||
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
||||||
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
||||||
|
|
||||||
|
expect(instance.$meta().inject()).not.toBeDefined()
|
||||||
|
expect(warn).toHaveBeenCalledTimes(1)
|
||||||
|
expect(instance.$meta().refresh()).not.toBeDefined()
|
||||||
|
expect(warn).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
instance.$meta().getOptions()
|
||||||
|
expect(warn).toHaveBeenCalledTimes(3)
|
||||||
|
warn.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('is loaded', () => {
|
||||||
|
const instance = new Vue({ metaInfo: {} })
|
||||||
|
expect(instance.$meta).toEqual(expect.any(Function))
|
||||||
|
|
||||||
|
expect(instance.$meta().inject).toEqual(expect.any(Function))
|
||||||
|
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
||||||
|
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
||||||
|
|
||||||
expect(instance.$meta().inject()).toBeDefined()
|
expect(instance.$meta().inject()).toBeDefined()
|
||||||
expect(instance.$meta().refresh()).toBeDefined()
|
expect(instance.$meta().refresh()).toBeDefined()
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import _updateClientMetaInfo from '../../src/client/updateClientMetaInfo'
|
|||||||
import { defaultOptions } from '../../src/shared/constants'
|
import { defaultOptions } from '../../src/shared/constants'
|
||||||
import metaInfoData from '../utils/meta-info-data'
|
import metaInfoData from '../utils/meta-info-data'
|
||||||
|
|
||||||
const updateClientMetaInfo = (type, data) => _updateClientMetaInfo(defaultOptions, { [type]: data })
|
const updateClientMetaInfo = (type, data) => _updateClientMetaInfo('test', defaultOptions, { [type]: data })
|
||||||
|
|
||||||
describe('updaters', () => {
|
describe('updaters', () => {
|
||||||
let html
|
let html
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const metaInfoData = {
|
|||||||
title: {
|
title: {
|
||||||
add: {
|
add: {
|
||||||
data: 'Hello World',
|
data: 'Hello World',
|
||||||
expect: ['<title data-vue-meta="true">Hello World</title>'],
|
expect: ['<title>Hello World</title>'],
|
||||||
test(side, defaultTest) {
|
test(side, defaultTest) {
|
||||||
if (side === 'client') {
|
if (side === 'client') {
|
||||||
// client side vue-meta uses document.title and doesnt change the html
|
// client side vue-meta uses document.title and doesnt change the html
|
||||||
@@ -26,11 +26,11 @@ const metaInfoData = {
|
|||||||
base: {
|
base: {
|
||||||
add: {
|
add: {
|
||||||
data: [{ href: 'href' }],
|
data: [{ href: 'href' }],
|
||||||
expect: ['<base data-vue-meta="true" href="href">']
|
expect: ['<base data-vue-meta="test" href="href">']
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
data: [{ href: 'href2' }],
|
data: [{ href: 'href2' }],
|
||||||
expect: ['<base data-vue-meta="true" href="href2">']
|
expect: ['<base data-vue-meta="test" href="href2">']
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: [],
|
data: [],
|
||||||
@@ -41,8 +41,8 @@ const metaInfoData = {
|
|||||||
add: {
|
add: {
|
||||||
data: [{ charset: 'utf-8' }, { property: 'a', content: 'a' }],
|
data: [{ charset: 'utf-8' }, { property: 'a', content: 'a' }],
|
||||||
expect: [
|
expect: [
|
||||||
'<meta data-vue-meta="true" charset="utf-8">',
|
'<meta data-vue-meta="test" charset="utf-8">',
|
||||||
'<meta data-vue-meta="true" property="a" content="a">'
|
'<meta data-vue-meta="test" property="a" content="a">'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
@@ -51,8 +51,8 @@ const metaInfoData = {
|
|||||||
{ property: 'a', content: 'b' }
|
{ property: 'a', content: 'b' }
|
||||||
],
|
],
|
||||||
expect: [
|
expect: [
|
||||||
'<meta data-vue-meta="true" charset="utf-16">',
|
'<meta data-vue-meta="test" charset="utf-16">',
|
||||||
'<meta data-vue-meta="true" property="a" content="b">'
|
'<meta data-vue-meta="test" property="a" content="b">'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// make sure elements that already exists are not unnecessarily updated
|
// make sure elements that already exists are not unnecessarily updated
|
||||||
@@ -62,8 +62,8 @@ const metaInfoData = {
|
|||||||
{ property: 'a', content: 'c' }
|
{ property: 'a', content: 'c' }
|
||||||
],
|
],
|
||||||
expect: [
|
expect: [
|
||||||
'<meta data-vue-meta="true" charset="utf-16">',
|
'<meta data-vue-meta="test" charset="utf-16">',
|
||||||
'<meta data-vue-meta="true" property="a" content="c">'
|
'<meta data-vue-meta="test" property="a" content="c">'
|
||||||
],
|
],
|
||||||
test(side, defaultTest) {
|
test(side, defaultTest) {
|
||||||
if (side === 'client') {
|
if (side === 'client') {
|
||||||
@@ -85,11 +85,11 @@ const metaInfoData = {
|
|||||||
link: {
|
link: {
|
||||||
add: {
|
add: {
|
||||||
data: [{ rel: 'stylesheet', href: 'href' }],
|
data: [{ rel: 'stylesheet', href: 'href' }],
|
||||||
expect: ['<link data-vue-meta="true" rel="stylesheet" href="href">']
|
expect: ['<link data-vue-meta="test" rel="stylesheet" href="href">']
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
data: [{ rel: 'stylesheet', href: 'href', media: 'screen' }],
|
data: [{ rel: 'stylesheet', href: 'href', media: 'screen' }],
|
||||||
expect: ['<link data-vue-meta="true" rel="stylesheet" href="href" media="screen">']
|
expect: ['<link data-vue-meta="test" rel="stylesheet" href="href" media="screen">']
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: [],
|
data: [],
|
||||||
@@ -99,11 +99,11 @@ const metaInfoData = {
|
|||||||
style: {
|
style: {
|
||||||
add: {
|
add: {
|
||||||
data: [{ type: 'text/css', cssText: '.foo { color: red; }' }],
|
data: [{ type: 'text/css', cssText: '.foo { color: red; }' }],
|
||||||
expect: ['<style data-vue-meta="true" type="text/css">.foo { color: red; }</style>']
|
expect: ['<style data-vue-meta="test" type="text/css">.foo { color: red; }</style>']
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
data: [{ type: 'text/css', cssText: '.foo { color: blue; }' }],
|
data: [{ type: 'text/css', cssText: '.foo { color: blue; }' }],
|
||||||
expect: ['<style data-vue-meta="true" type="text/css">.foo { color: blue; }</style>']
|
expect: ['<style data-vue-meta="test" type="text/css">.foo { color: blue; }</style>']
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: [],
|
data: [],
|
||||||
@@ -117,8 +117,8 @@ const metaInfoData = {
|
|||||||
{ src: 'src', async: true, defer: true, body: true }
|
{ src: 'src', async: true, defer: true, body: true }
|
||||||
],
|
],
|
||||||
expect: [
|
expect: [
|
||||||
'<script data-vue-meta="true" src="src" async defer data-vmid="content"></script>',
|
'<script data-vue-meta="test" src="src" async defer data-vmid="content"></script>',
|
||||||
'<script data-vue-meta="true" src="src" async defer data-body="true"></script>'
|
'<script data-vue-meta="test" src="src" async defer data-body="true"></script>'
|
||||||
],
|
],
|
||||||
test(side, defaultTest) {
|
test(side, defaultTest) {
|
||||||
return () => {
|
return () => {
|
||||||
@@ -145,7 +145,7 @@ const metaInfoData = {
|
|||||||
// this test only runs for client so we can directly expect wrong boolean attributes
|
// this test only runs for client so we can directly expect wrong boolean attributes
|
||||||
change: {
|
change: {
|
||||||
data: [{ src: 'src', async: true, defer: true, [defaultOptions.tagIDKeyName]: 'content2' }],
|
data: [{ src: 'src', async: true, defer: true, [defaultOptions.tagIDKeyName]: 'content2' }],
|
||||||
expect: ['<script data-vue-meta="true" src="src" async="true" defer="true" data-vmid="content2"></script>']
|
expect: ['<script data-vue-meta="test" src="src" async="true" defer="true" data-vmid="content2"></script>']
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: [],
|
data: [],
|
||||||
@@ -155,11 +155,11 @@ const metaInfoData = {
|
|||||||
noscript: {
|
noscript: {
|
||||||
add: {
|
add: {
|
||||||
data: [{ innerHTML: '<p>noscript</p>' }],
|
data: [{ innerHTML: '<p>noscript</p>' }],
|
||||||
expect: ['<noscript data-vue-meta="true"><p>noscript</p></noscript>']
|
expect: ['<noscript data-vue-meta="test"><p>noscript</p></noscript>']
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
data: [{ innerHTML: '<p>noscript, no really</p>' }],
|
data: [{ innerHTML: '<p>noscript, no really</p>' }],
|
||||||
expect: ['<noscript data-vue-meta="true"><p>noscript, no really</p></noscript>']
|
expect: ['<noscript data-vue-meta="test"><p>noscript, no really</p></noscript>']
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: [],
|
data: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user