mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-23 16:00:34 +03:00
chore: dist size improvements
chore: configure terser to mangle internal properties chore: do not use default value assignment in function args
This commit is contained in:
@@ -5,6 +5,7 @@ import babel from 'rollup-plugin-babel'
|
|||||||
import replace from 'rollup-plugin-replace'
|
import replace from 'rollup-plugin-replace'
|
||||||
import { terser } from 'rollup-plugin-terser'
|
import { terser } from 'rollup-plugin-terser'
|
||||||
import defaultsDeep from 'lodash/defaultsDeep'
|
import defaultsDeep from 'lodash/defaultsDeep'
|
||||||
|
import { defaultOptions } from '../src/shared/constants'
|
||||||
|
|
||||||
const pkg = require('../package.json')
|
const pkg = require('../package.json')
|
||||||
|
|
||||||
@@ -35,6 +36,41 @@ const babelConfig = () => ({
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const internalObjectProperties = [
|
||||||
|
// Plugin options
|
||||||
|
// NOTE, see shared/options for why/how this is possible to do
|
||||||
|
...Object.keys(defaultOptions),
|
||||||
|
'refreshOnceOnNavigation',
|
||||||
|
// Runtime state props on $root._vueMeta
|
||||||
|
'appId',
|
||||||
|
'pausing',
|
||||||
|
'navGuards',
|
||||||
|
'initialized',
|
||||||
|
'initializing',
|
||||||
|
'deprecationWarningShown',
|
||||||
|
// updateClientMetaInfo return props
|
||||||
|
'tagsAdded',
|
||||||
|
'tagsRemoved',
|
||||||
|
// escapeOptions
|
||||||
|
'doEscape',
|
||||||
|
// deepmerge
|
||||||
|
'isMergeableObject',
|
||||||
|
'arrayMerge'
|
||||||
|
]
|
||||||
|
|
||||||
|
const terserOpts = {
|
||||||
|
nameCache: {},
|
||||||
|
mangle: {
|
||||||
|
properties: {
|
||||||
|
//debug: '___DEBUGGGG___',
|
||||||
|
// minimize all object properties except when they are quotes like obj['prop']
|
||||||
|
keep_quoted: "strict",
|
||||||
|
// and minimize props listed in internalObjectProperties
|
||||||
|
regex: new RegExp(`^(${internalObjectProperties.join('|')})$`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function rollupConfig({
|
function rollupConfig({
|
||||||
plugins = [],
|
plugins = [],
|
||||||
...config
|
...config
|
||||||
@@ -51,17 +87,18 @@ function rollupConfig({
|
|||||||
'process.env.VERSION': `"${version}"`,
|
'process.env.VERSION': `"${version}"`,
|
||||||
'process.server' : isBrowserBuild ? 'false' : 'true',
|
'process.server' : isBrowserBuild ? 'false' : 'true',
|
||||||
/* remove unused stuff from deepmerge */
|
/* remove unused stuff from deepmerge */
|
||||||
|
|
||||||
// remove react stuff from is-mergeable-object
|
// remove react stuff from is-mergeable-object
|
||||||
'|| isReactElement(value)': '|| false',
|
'|| isReactElement(value)': '|| false',
|
||||||
// we always provide an arrayMerge, remove default
|
// we always provide an arrayMerge, remove default
|
||||||
'|| defaultArrayMerge' : '',
|
'|| defaultArrayMerge' : '',
|
||||||
// we dont provide a custom merge
|
// clone is a deprecated option we dont use
|
||||||
'options.clone' : 'false',
|
'options.clone' : 'false',
|
||||||
// we dont provide a custom merge
|
// we dont provide a custom merge
|
||||||
'options.customMerge' : 'false',
|
'options.customMerge' : 'false',
|
||||||
// dont use this
|
// we dont use this helper
|
||||||
'deepmerge.all = ' : 'false;'
|
'deepmerge.all = ' : 'false;',
|
||||||
|
// we dont use symbols on our objects
|
||||||
|
'.concat(getEnumerableOwnPropertySymbols(target))': ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +140,7 @@ export default [
|
|||||||
file: pkg.web.replace('.js', '.min.js'),
|
file: pkg.web.replace('.js', '.min.js'),
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
terser()
|
terser(terserOpts)
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// common js build
|
// common js build
|
||||||
@@ -137,7 +174,7 @@ export default [
|
|||||||
format: 'es'
|
format: 'es'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
terser()
|
terser(terserOpts)
|
||||||
],
|
],
|
||||||
external: Object.keys(pkg.dependencies)
|
external: Object.keys(pkg.dependencies)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -1,13 +1,14 @@
|
|||||||
import { toArray } from '../utils/array'
|
import { toArray } from '../utils/array'
|
||||||
|
import { querySelector, removeAttribute } from '../utils/elements'
|
||||||
|
|
||||||
const callbacks = []
|
const callbacks = []
|
||||||
|
|
||||||
export function isDOMLoaded (d = document) {
|
export function isDOMLoaded (d) {
|
||||||
return d.readyState !== 'loading'
|
return (d || document).readyState !== 'loading'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDOMComplete (d = document) {
|
export function isDOMComplete (d) {
|
||||||
return d.readyState === 'complete'
|
return (d || document).readyState === 'complete'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitDOMLoaded () {
|
export function waitDOMLoaded () {
|
||||||
@@ -69,7 +70,7 @@ export function applyCallbacks (matchElement) {
|
|||||||
|
|
||||||
let elements = []
|
let elements = []
|
||||||
if (!matchElement) {
|
if (!matchElement) {
|
||||||
elements = toArray(document.querySelectorAll(selector))
|
elements = toArray(querySelector(selector))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchElement && matchElement.matches(selector)) {
|
if (matchElement && matchElement.matches(selector)) {
|
||||||
@@ -95,7 +96,7 @@ export function applyCallbacks (matchElement) {
|
|||||||
* attribute after ssr and if we dont remove it the node
|
* attribute after ssr and if we dont remove it the node
|
||||||
* will fail isEqualNode on the client
|
* will fail isEqualNode on the client
|
||||||
*/
|
*/
|
||||||
element.removeAttribute('onload')
|
removeAttribute(element, 'onload')
|
||||||
|
|
||||||
callback(element)
|
callback(element)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-4
@@ -17,7 +17,9 @@ import updateClientMetaInfo from './updateClientMetaInfo'
|
|||||||
*
|
*
|
||||||
* @return {Object} - new meta info
|
* @return {Object} - new meta info
|
||||||
*/
|
*/
|
||||||
export default function refresh (rootVm, options = {}) {
|
export default function refresh (rootVm, options) {
|
||||||
|
options = options || {}
|
||||||
|
|
||||||
// make sure vue-meta was initiated
|
// make sure vue-meta was initiated
|
||||||
if (!rootVm[rootConfigKey]) {
|
if (!rootVm[rootConfigKey]) {
|
||||||
showWarningNotSupported()
|
showWarningNotSupported()
|
||||||
@@ -30,11 +32,16 @@ export default function refresh (rootVm, options = {}) {
|
|||||||
const metaInfo = getMetaInfo(options, rawInfo, clientSequences, rootVm)
|
const metaInfo = getMetaInfo(options, rawInfo, clientSequences, rootVm)
|
||||||
|
|
||||||
const { appId } = rootVm[rootConfigKey]
|
const { appId } = rootVm[rootConfigKey]
|
||||||
const tags = updateClientMetaInfo(appId, options, metaInfo)
|
let 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.tagsAdded, tags.tagsRemoved)
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
addedTags: tags.tagsAdded,
|
||||||
|
removedTags: tags.tagsRemoved
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const appsMetaInfo = getAppsMetaInfo()
|
const appsMetaInfo = getAppsMetaInfo()
|
||||||
@@ -46,5 +53,9 @@ export default function refresh (rootVm, options = {}) {
|
|||||||
clearAppsMetaInfo(true)
|
clearAppsMetaInfo(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { vm: rootVm, metaInfo, tags }
|
return {
|
||||||
|
vm: rootVm,
|
||||||
|
metaInfo: metaInfo, // eslint-disable-line object-shorthand
|
||||||
|
tags
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function triggerUpdate (rootVm, hookName) {
|
|||||||
rootVm[rootConfigKey].initialized = null
|
rootVm[rootConfigKey].initialized = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rootVm[rootConfigKey].initialized && !rootVm[rootConfigKey].paused) {
|
if (rootVm[rootConfigKey].initialized && !rootVm[rootConfigKey].pausing) {
|
||||||
// batch potential DOM updates to prevent extraneous re-rendering
|
// batch potential DOM updates to prevent extraneous re-rendering
|
||||||
batchUpdate(() => rootVm.$meta().refresh())
|
batchUpdate(() => rootVm.$meta().refresh())
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,9 @@ export function triggerUpdate (rootVm, hookName) {
|
|||||||
* @param {Function} callback - the update to perform
|
* @param {Function} callback - the update to perform
|
||||||
* @return {Number} id - a new ID
|
* @return {Number} id - a new ID
|
||||||
*/
|
*/
|
||||||
export function batchUpdate (callback, timeout = 10) {
|
export function batchUpdate (callback, timeout) {
|
||||||
|
timeout = timeout || 10
|
||||||
|
|
||||||
clearTimeout(batchId)
|
clearTimeout(batchId)
|
||||||
|
|
||||||
batchId = setTimeout(() => {
|
batchId = setTimeout(() => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { metaInfoOptionKeys, metaInfoAttributeKeys, tagsSupportingOnload } from '../shared/constants'
|
import { metaInfoOptionKeys, metaInfoAttributeKeys, tagsSupportingOnload } from '../shared/constants'
|
||||||
import { isArray } from '../utils/is-type'
|
import { isArray } from '../utils/is-type'
|
||||||
import { includes } from '../utils/array'
|
import { includes } from '../utils/array'
|
||||||
import { getTag } from '../utils/elements'
|
import { getTag, removeAttribute } from '../utils/elements'
|
||||||
import { addCallbacks, addListeners } from './load'
|
import { addCallbacks, addListeners } from './load'
|
||||||
import { updateAttribute, updateTag, updateTitle } from './updaters'
|
import { updateAttribute, updateTag, updateTitle } from './updaters'
|
||||||
|
|
||||||
@@ -10,7 +10,8 @@ import { updateAttribute, updateTag, updateTitle } from './updaters'
|
|||||||
*
|
*
|
||||||
* @param {Object} newInfo - the meta info to update to
|
* @param {Object} newInfo - the meta info to update to
|
||||||
*/
|
*/
|
||||||
export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
export default function updateClientMetaInfo (appId, options, newInfo) {
|
||||||
|
options = options || {}
|
||||||
const { ssrAttribute, ssrAppId } = options
|
const { ssrAttribute, ssrAppId } = options
|
||||||
|
|
||||||
// only cache tags for current update
|
// only cache tags for current update
|
||||||
@@ -21,7 +22,7 @@ export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
|||||||
// if this is a server render, then dont update
|
// if this is a server render, then dont update
|
||||||
if (appId === ssrAppId && htmlTag.hasAttribute(ssrAttribute)) {
|
if (appId === ssrAppId && 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)
|
removeAttribute(htmlTag, ssrAttribute)
|
||||||
|
|
||||||
// add load callbacks if the
|
// add load callbacks if the
|
||||||
let addLoadListeners = false
|
let addLoadListeners = false
|
||||||
@@ -39,8 +40,8 @@ export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialize tracked changes
|
// initialize tracked changes
|
||||||
const addedTags = {}
|
const tagsAdded = {}
|
||||||
const removedTags = {}
|
const tagsRemoved = {}
|
||||||
|
|
||||||
for (const type in newInfo) {
|
for (const type in newInfo) {
|
||||||
// ignore these
|
// ignore these
|
||||||
@@ -75,10 +76,10 @@ export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (newTags.length) {
|
if (newTags.length) {
|
||||||
addedTags[type] = newTags
|
tagsAdded[type] = newTags
|
||||||
removedTags[type] = oldTags
|
tagsRemoved[type] = oldTags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { addedTags, removedTags }
|
return { tagsAdded, tagsRemoved }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { booleanHtmlAttributes } from '../../shared/constants'
|
import { booleanHtmlAttributes } from '../../shared/constants'
|
||||||
import { includes } from '../../utils/array'
|
import { includes } from '../../utils/array'
|
||||||
|
import { removeAttribute } from '../../utils/elements'
|
||||||
|
|
||||||
// keep a local map of attribute values
|
// keep a local map of attribute values
|
||||||
// instead of adding it to the html
|
// instead of adding it to the html
|
||||||
@@ -11,11 +12,13 @@ export const attributeMap = {}
|
|||||||
* @param {Object} attrs - the new document html attributes
|
* @param {Object} attrs - the new document html attributes
|
||||||
* @param {HTMLElement} tag - the HTMLElement tag to update with new attrs
|
* @param {HTMLElement} tag - the HTMLElement tag to update with new attrs
|
||||||
*/
|
*/
|
||||||
export default function updateAttribute (appId, { attribute } = {}, type, attrs, tag) {
|
export default function updateAttribute (appId, options, type, attrs, tag) {
|
||||||
|
const { attribute } = options || {}
|
||||||
|
|
||||||
const vueMetaAttrString = tag.getAttribute(attribute)
|
const vueMetaAttrString = tag.getAttribute(attribute)
|
||||||
if (vueMetaAttrString) {
|
if (vueMetaAttrString) {
|
||||||
attributeMap[type] = JSON.parse(decodeURI(vueMetaAttrString))
|
attributeMap[type] = JSON.parse(decodeURI(vueMetaAttrString))
|
||||||
tag.removeAttribute(attribute)
|
removeAttribute(tag, attribute)
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = attributeMap[type] || {}
|
const data = attributeMap[type] || {}
|
||||||
@@ -62,7 +65,7 @@ export default function updateAttribute (appId, { attribute } = {}, type, attrs,
|
|||||||
|
|
||||||
tag.setAttribute(attr, attrValue)
|
tag.setAttribute(attr, attrValue)
|
||||||
} else {
|
} else {
|
||||||
tag.removeAttribute(attr)
|
removeAttribute(tag, attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -10,8 +10,8 @@ import { queryElements, getElementsKey } from '../../utils/elements.js'
|
|||||||
* @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 (appId, options = {}, type, tags, head, body) {
|
export default function updateTag (appId, options, type, tags, head, body) {
|
||||||
const { attribute, tagIDKeyName } = options
|
const { attribute, tagIDKeyName } = options || {}
|
||||||
|
|
||||||
const dataAttributes = commonDataAttributes.slice()
|
const dataAttributes = commonDataAttributes.slice()
|
||||||
dataAttributes.push(tagIDKeyName)
|
dataAttributes.push(tagIDKeyName)
|
||||||
@@ -46,20 +46,20 @@ export default function updateTag (appId, options = {}, type, tags, head, body)
|
|||||||
const newElement = document.createElement(type)
|
const newElement = document.createElement(type)
|
||||||
newElement.setAttribute(attribute, appId)
|
newElement.setAttribute(attribute, appId)
|
||||||
|
|
||||||
for (const attr in tag) {
|
Object.keys(tag).forEach((attr) => {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (!tag.hasOwnProperty(attr) || includes(tagProperties, attr)) {
|
if (includes(tagProperties, attr)) {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attr === 'innerHTML') {
|
if (attr === 'innerHTML') {
|
||||||
newElement.innerHTML = tag.innerHTML
|
newElement.innerHTML = tag.innerHTML
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attr === 'json') {
|
if (attr === 'json') {
|
||||||
newElement.innerHTML = JSON.stringify(tag.json)
|
newElement.innerHTML = JSON.stringify(tag.json)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attr === 'cssText') {
|
if (attr === 'cssText') {
|
||||||
@@ -69,12 +69,12 @@ export default function updateTag (appId, options = {}, type, tags, head, body)
|
|||||||
} else {
|
} else {
|
||||||
newElement.appendChild(document.createTextNode(tag.cssText))
|
newElement.appendChild(document.createTextNode(tag.cssText))
|
||||||
}
|
}
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attr === 'callback') {
|
if (attr === 'callback') {
|
||||||
newElement.onload = () => tag[attr](newElement)
|
newElement.onload = () => tag[attr](newElement)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const _attr = includes(dataAttributes, attr)
|
const _attr = includes(dataAttributes, attr)
|
||||||
@@ -83,12 +83,12 @@ export default function updateTag (appId, options = {}, type, tags, head, body)
|
|||||||
|
|
||||||
const isBooleanAttribute = includes(booleanHtmlAttributes, attr)
|
const isBooleanAttribute = includes(booleanHtmlAttributes, attr)
|
||||||
if (isBooleanAttribute && !tag[attr]) {
|
if (isBooleanAttribute && !tag[attr]) {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = isBooleanAttribute ? '' : tag[attr]
|
const value = isBooleanAttribute ? '' : tag[attr]
|
||||||
newElement.setAttribute(_attr, value)
|
newElement.setAttribute(_attr, value)
|
||||||
}
|
})
|
||||||
|
|
||||||
const oldElements = currentElements[getElementsKey(tag)]
|
const oldElements = currentElements[getElementsKey(tag)]
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import { hasMetaInfo } from './shared/meta-helpers'
|
|||||||
* Plugin install function.
|
* Plugin install function.
|
||||||
* @param {Function} Vue - the Vue constructor.
|
* @param {Function} Vue - the Vue constructor.
|
||||||
*/
|
*/
|
||||||
function install (Vue, options = {}) {
|
function install (Vue, options) {
|
||||||
if (Vue.__vuemeta_installed) {
|
if (Vue.__vuemeta_installed) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { serverSequences } from '../shared/escaping'
|
|||||||
import { setOptions } from '../shared/options'
|
import { setOptions } from '../shared/options'
|
||||||
import generateServerInjector from './generateServerInjector'
|
import generateServerInjector from './generateServerInjector'
|
||||||
|
|
||||||
export default function generate (rawInfo, options = {}) {
|
export default function generate (rawInfo, options) {
|
||||||
const metaInfo = getMetaInfo(setOptions(options), rawInfo, serverSequences)
|
options = setOptions(options)
|
||||||
|
const metaInfo = getMetaInfo(options, rawInfo, serverSequences)
|
||||||
|
|
||||||
const serverInjector = generateServerInjector(options, metaInfo)
|
const serverInjector = generateServerInjector(options, metaInfo)
|
||||||
return serverInjector.injectors
|
return serverInjector.injectors
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import { booleanHtmlAttributes } from '../../shared/constants'
|
|||||||
* @param {Object} data - the attributes to generate
|
* @param {Object} data - the attributes to generate
|
||||||
* @return {Object} - the attribute generator
|
* @return {Object} - the attribute generator
|
||||||
*/
|
*/
|
||||||
export default function attributeGenerator ({ attribute, ssrAttribute } = {}, type, data, addSrrAttribute) {
|
export default function attributeGenerator (options, type, data, addSrrAttribute) {
|
||||||
|
const { attribute, ssrAttribute } = options || {}
|
||||||
let attributeStr = ''
|
let attributeStr = ''
|
||||||
|
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import {
|
|||||||
* @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 ({ ssrAppId, attribute, tagIDKeyName } = {}, type, tags, { appId, body = false, pbody = false, ln = false } = {}) {
|
export default function tagGenerator (options, type, tags, generatorOptions) {
|
||||||
|
const { ssrAppId, attribute, tagIDKeyName } = options || {}
|
||||||
|
const { appId, body = false, pbody = false, ln = false } = generatorOptions || {}
|
||||||
|
|
||||||
const dataAttributes = [tagIDKeyName, ...commonDataAttributes]
|
const dataAttributes = [tagIDKeyName, ...commonDataAttributes]
|
||||||
|
|
||||||
if (!tags || !tags.length) {
|
if (!tags || !tags.length) {
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
* @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, { ln } = {}) {
|
export default function titleGenerator (options, type, data, generatorOptions) {
|
||||||
|
const { ln } = generatorOptions || {}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import generateServerInjector from './generateServerInjector'
|
|||||||
* @vm {Object} - Vue instance - ideally the root component
|
* @vm {Object} - Vue instance - ideally the root component
|
||||||
* @return {Object} - server meta info with `toString` methods
|
* @return {Object} - server meta info with `toString` methods
|
||||||
*/
|
*/
|
||||||
export default function inject (rootVm, options = {}) {
|
export default function inject (rootVm, options) {
|
||||||
// make sure vue-meta was initiated
|
// make sure vue-meta was initiated
|
||||||
if (!rootVm[rootConfigKey]) {
|
if (!rootVm[rootConfigKey]) {
|
||||||
showWarningNotSupported()
|
showWarningNotSupported()
|
||||||
|
|||||||
+12
-9
@@ -6,7 +6,8 @@ import { addNavGuards } from './nav-guards'
|
|||||||
import { pause, resume } from './pausing'
|
import { pause, resume } from './pausing'
|
||||||
import { getOptions } from './options'
|
import { getOptions } from './options'
|
||||||
|
|
||||||
export default function $meta (options = {}) {
|
export default function $meta (options) {
|
||||||
|
options = options || {}
|
||||||
/**
|
/**
|
||||||
* Returns an injector for server-side rendering.
|
* Returns an injector for server-side rendering.
|
||||||
* @this {Object} - the Vue instance (a root component)
|
* @this {Object} - the Vue instance (a root component)
|
||||||
@@ -15,16 +16,18 @@ export default function $meta (options = {}) {
|
|||||||
const $root = this.$root
|
const $root = this.$root
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getOptions: () => getOptions(options),
|
'getOptions': () => getOptions(options),
|
||||||
setOptions: ({ refreshOnceOnNavigation } = {}) => {
|
'setOptions': (newOptions) => {
|
||||||
if (refreshOnceOnNavigation) {
|
const refreshNavKey = 'refreshOnceOnNavigation'
|
||||||
|
if (newOptions && newOptions[refreshNavKey]) {
|
||||||
|
options.refreshOnceOnNavigation = newOptions[refreshNavKey]
|
||||||
addNavGuards($root)
|
addNavGuards($root)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
refresh: () => refresh($root, options),
|
'refresh': () => refresh($root, options),
|
||||||
inject: () => process.server ? inject($root, options) : showWarningNotSupportedInBrowserBundle('inject'),
|
'inject': () => process.server ? inject($root, options) : showWarningNotSupportedInBrowserBundle('inject'),
|
||||||
pause: () => pause($root),
|
'pause': () => pause($root),
|
||||||
resume: () => resume($root),
|
'resume': () => resume($root),
|
||||||
addApp: appId => addApp($root, appId, options)
|
'addApp': appId => addApp($root, appId, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-7
@@ -59,25 +59,28 @@ export const defaultOptions = {
|
|||||||
ssrAppId
|
ssrAppId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// might be a bit ugly, but minimizes the browser bundles a bit
|
||||||
|
const defaultInfoKeys = Object.keys(defaultInfo)
|
||||||
|
|
||||||
// The metaInfo property keys which are used to disable escaping
|
// The metaInfo property keys which are used to disable escaping
|
||||||
export const disableOptionKeys = [
|
export const disableOptionKeys = [
|
||||||
'__dangerouslyDisableSanitizers',
|
defaultInfoKeys[12],
|
||||||
'__dangerouslyDisableSanitizersByTagID'
|
defaultInfoKeys[13]
|
||||||
]
|
]
|
||||||
|
|
||||||
// List of metaInfo property keys which are configuration options (and dont generate html)
|
// List of metaInfo property keys which are configuration options (and dont generate html)
|
||||||
export const metaInfoOptionKeys = [
|
export const metaInfoOptionKeys = [
|
||||||
'titleChunk',
|
defaultInfoKeys[1],
|
||||||
'titleTemplate',
|
defaultInfoKeys[2],
|
||||||
'changed',
|
'changed',
|
||||||
...disableOptionKeys
|
...disableOptionKeys
|
||||||
]
|
]
|
||||||
|
|
||||||
// List of metaInfo property keys which only generates attributes and no tags
|
// List of metaInfo property keys which only generates attributes and no tags
|
||||||
export const metaInfoAttributeKeys = [
|
export const metaInfoAttributeKeys = [
|
||||||
'htmlAttrs',
|
defaultInfoKeys[3],
|
||||||
'headAttrs',
|
defaultInfoKeys[4],
|
||||||
'bodyAttrs'
|
defaultInfoKeys[5]
|
||||||
]
|
]
|
||||||
|
|
||||||
// HTML elements which support the onload event
|
// HTML elements which support the onload event
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ export function escape (info, options, escapeOptions, escapeKeys) {
|
|||||||
return escaped
|
return escaped
|
||||||
}
|
}
|
||||||
|
|
||||||
export function escapeMetaInfo (options, info, escapeSequences = []) {
|
export function escapeMetaInfo (options, info, escapeSequences) {
|
||||||
|
escapeSequences = escapeSequences || []
|
||||||
// do not use destructuring for seq, it increases transpiled size
|
// do not use destructuring for seq, it increases transpiled size
|
||||||
// due to var checks while we are guaranteed the structure of the cb
|
// due to var checks while we are guaranteed the structure of the cb
|
||||||
const escapeOptions = {
|
const escapeOptions = {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { defaultInfo } from './constants'
|
|||||||
import { merge } from './merge'
|
import { merge } from './merge'
|
||||||
import { inMetaInfoBranch } from './meta-helpers'
|
import { inMetaInfoBranch } from './meta-helpers'
|
||||||
|
|
||||||
export function getComponentMetaInfo (options = {}, component) {
|
export function getComponentMetaInfo (options, component) {
|
||||||
return getComponentOption(options, component, defaultInfo)
|
return getComponentOption(options || {}, component, defaultInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,14 +21,17 @@ export function getComponentMetaInfo (options = {}, component) {
|
|||||||
* @param {Object} [result={}] - result so far
|
* @param {Object} [result={}] - result so far
|
||||||
* @return {Object} result - final aggregated result
|
* @return {Object} result - final aggregated result
|
||||||
*/
|
*/
|
||||||
export function getComponentOption (options = {}, component, result = {}) {
|
export function getComponentOption (options, component, result) {
|
||||||
const { keyName } = options
|
result = result || {}
|
||||||
const { $metaInfo, $options, $children } = component
|
|
||||||
|
|
||||||
if (component._inactive) {
|
if (component._inactive) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options = options || {}
|
||||||
|
const { keyName } = options
|
||||||
|
const { $metaInfo, $options, $children } = component
|
||||||
|
|
||||||
// only collect option data if it exists
|
// only collect option data if it exists
|
||||||
if ($options[keyName]) {
|
if ($options[keyName]) {
|
||||||
// if $metaInfo exists then [keyName] was defined as a function
|
// if $metaInfo exists then [keyName] was defined as a function
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import { applyTemplate } from './template'
|
|||||||
* @param {Object} component - the Vue instance to get meta info from
|
* @param {Object} component - the Vue instance to get meta info from
|
||||||
* @return {Object} - returned meta info
|
* @return {Object} - returned meta info
|
||||||
*/
|
*/
|
||||||
export default function getMetaInfo (options = {}, info, escapeSequences = [], component) {
|
export default function getMetaInfo (options, info, escapeSequences, component) {
|
||||||
|
options = options || {}
|
||||||
|
escapeSequences = escapeSequences || []
|
||||||
|
|
||||||
const { tagIDKeyName } = options
|
const { tagIDKeyName } = options
|
||||||
// Remove all "template" tags from meta
|
// Remove all "template" tags from meta
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ export default function getMetaInfo (options = {}, info, escapeSequences = [], c
|
|||||||
if (info.meta) {
|
if (info.meta) {
|
||||||
// remove meta items with duplicate vmid's
|
// remove meta items with duplicate vmid's
|
||||||
info.meta = info.meta.filter((metaItem, index, arr) => {
|
info.meta = info.meta.filter((metaItem, index, arr) => {
|
||||||
const hasVmid = metaItem.hasOwnProperty(tagIDKeyName)
|
const hasVmid = !!metaItem[tagIDKeyName]
|
||||||
if (!hasVmid) {
|
if (!hasVmid) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-6
@@ -33,8 +33,10 @@ export function arrayMerge ({ component, tagIDKeyName, metaTemplateKeyName, cont
|
|||||||
// when sourceItem explictly defines contentKeyName or innerHTML as undefined, its
|
// when sourceItem explictly defines contentKeyName or innerHTML as undefined, its
|
||||||
// an indication that we need to skip the default behaviour or child has preference over parent
|
// an indication that we need to skip the default behaviour or child has preference over parent
|
||||||
// which means we keep the targetItem and ignore/remove the sourceItem
|
// which means we keep the targetItem and ignore/remove the sourceItem
|
||||||
if ((sourceItem.hasOwnProperty(contentKeyName) && sourceItem[contentKeyName] === undefined) ||
|
if (
|
||||||
(sourceItem.hasOwnProperty('innerHTML') && sourceItem.innerHTML === undefined)) {
|
(contentKeyName in sourceItem && sourceItem[contentKeyName] === undefined) ||
|
||||||
|
('innerHTML' in sourceItem && sourceItem.innerHTML === undefined)
|
||||||
|
) {
|
||||||
destination.push(targetItem)
|
destination.push(targetItem)
|
||||||
// remove current index from source array so its not concatenated to destination below
|
// remove current index from source array so its not concatenated to destination below
|
||||||
source.splice(sourceIndex, 1)
|
source.splice(sourceIndex, 1)
|
||||||
@@ -75,11 +77,15 @@ export function arrayMerge ({ component, tagIDKeyName, metaTemplateKeyName, cont
|
|||||||
return destination.concat(source)
|
return destination.concat(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function merge (target, source, options = {}) {
|
let warningShown = false
|
||||||
|
|
||||||
|
export function merge (target, source, options) {
|
||||||
|
options = options || {}
|
||||||
|
|
||||||
// remove properties explicitly set to false so child components can
|
// remove properties explicitly set to false so child components can
|
||||||
// optionally _not_ overwrite the parents content
|
// optionally _not_ overwrite the parents content
|
||||||
// (for array properties this is checked in arrayMerge)
|
// (for array properties this is checked in arrayMerge)
|
||||||
if (source.hasOwnProperty('title') && source.title === undefined) {
|
if (source.title === undefined) {
|
||||||
delete source.title
|
delete source.title
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,9 +95,10 @@ export function merge (target, source, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const key in source[attrKey]) {
|
for (const key in source[attrKey]) {
|
||||||
if (source[attrKey].hasOwnProperty(key) && source[attrKey][key] === undefined) {
|
if (key in source[attrKey] && source[attrKey][key] === undefined) {
|
||||||
if (includes(booleanHtmlAttributes, key)) {
|
if (includes(booleanHtmlAttributes, key) && !warningShown) {
|
||||||
warn('VueMeta: Please note that since v2 the value undefined is not used to indicate boolean attributes anymore, see migration guide for details')
|
warn('VueMeta: Please note that since v2 the value undefined is not used to indicate boolean attributes anymore, see migration guide for details')
|
||||||
|
warningShown = true
|
||||||
}
|
}
|
||||||
delete source[attrKey][key]
|
delete source[attrKey][key]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import { isUndefined, isObject } from '../utils/is-type'
|
|||||||
import { rootConfigKey } from './constants'
|
import { rootConfigKey } from './constants'
|
||||||
|
|
||||||
// Vue $root instance has a _vueMeta object property, otherwise its a boolean true
|
// Vue $root instance has a _vueMeta object property, otherwise its a boolean true
|
||||||
export function hasMetaInfo (vm = this) {
|
export function hasMetaInfo (vm) {
|
||||||
|
vm = vm || this
|
||||||
return vm && (vm[rootConfigKey] === true || isObject(vm[rootConfigKey]))
|
return vm && (vm[rootConfigKey] === true || isObject(vm[rootConfigKey]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// a component is in a metaInfo branch when itself has meta info or one of its (grand-)children has
|
// a component is in a metaInfo branch when itself has meta info or one of its (grand-)children has
|
||||||
export function inMetaInfoBranch (vm = this) {
|
export function inMetaInfoBranch (vm) {
|
||||||
|
vm = vm || this
|
||||||
return vm && !isUndefined(vm[rootConfigKey])
|
return vm && !isUndefined(vm[rootConfigKey])
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-20
@@ -15,19 +15,22 @@ export default function createMixin (Vue, options) {
|
|||||||
// watch for client side component updates
|
// watch for client side component updates
|
||||||
return {
|
return {
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
const $root = this.$root
|
// https://github.com/terser/terser/issues/458
|
||||||
const $options = this.$options
|
const $this = this
|
||||||
const $isServer = this.$isServer
|
const $root = $this.$root
|
||||||
|
const $options = $this.$options
|
||||||
|
const $isServer = $this.$isServer
|
||||||
|
const $nextTick = $this.$nextTick
|
||||||
|
|
||||||
Object.defineProperty(this, '_hasMetaInfo', {
|
Object.defineProperty($this, '_hasMetaInfo', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get () {
|
get () {
|
||||||
// Show deprecation warning once when devtools enabled
|
// Show deprecation warning once when devtools enabled
|
||||||
if (Vue.config.devtools && !$root[rootConfigKey]._shown) {
|
if (Vue.config.devtools && !$root[rootConfigKey].deprecationWarningShown) {
|
||||||
warn('VueMeta DeprecationWarning: _hasMetaInfo has been deprecated and will be removed in a future version. Please use hasMetaInfo(vm) instead')
|
warn('VueMeta DeprecationWarning: _hasMetaInfo has been deprecated and will be removed in a future version. Please use hasMetaInfo(vm) instead')
|
||||||
$root[rootConfigKey]._shown = true
|
$root[rootConfigKey].deprecationWarningShown = true
|
||||||
}
|
}
|
||||||
return hasMetaInfo(this)
|
return hasMetaInfo($this)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -45,10 +48,10 @@ export default function createMixin (Vue, options) {
|
|||||||
|
|
||||||
// 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
|
||||||
// if _vueMeta = true it has info, if _vueMeta = false a child has info
|
// if _vueMeta = true it has info, if _vueMeta = false a child has info
|
||||||
if (!this[rootConfigKey]) {
|
if (!$this[rootConfigKey]) {
|
||||||
this[rootConfigKey] = true
|
$this[rootConfigKey] = true
|
||||||
|
|
||||||
let parent = this.$parent
|
let parent = $this.$parent
|
||||||
while (parent && parent !== $root) {
|
while (parent && parent !== $root) {
|
||||||
if (isUndefined(parent[rootConfigKey])) {
|
if (isUndefined(parent[rootConfigKey])) {
|
||||||
parent[rootConfigKey] = false
|
parent[rootConfigKey] = false
|
||||||
@@ -60,9 +63,7 @@ export default function createMixin (Vue, options) {
|
|||||||
// coerce function-style metaInfo to a computed prop so we can observe
|
// coerce function-style metaInfo to a computed prop so we can observe
|
||||||
// it on creation
|
// it on creation
|
||||||
if (isFunction($options[options.keyName])) {
|
if (isFunction($options[options.keyName])) {
|
||||||
if (!$options.computed) {
|
$options.computed = $options.computed || {}
|
||||||
$options.computed = {}
|
|
||||||
}
|
|
||||||
$options.computed.$metaInfo = $options[options.keyName]
|
$options.computed.$metaInfo = $options[options.keyName]
|
||||||
|
|
||||||
if (!$isServer) {
|
if (!$isServer) {
|
||||||
@@ -70,7 +71,7 @@ export default function createMixin (Vue, options) {
|
|||||||
// when it changes (i.e. automatically handle async actions that affect metaInfo)
|
// when it changes (i.e. automatically handle async actions that affect metaInfo)
|
||||||
// credit for this suggestion goes to [Sébastien Chopin](https://github.com/Atinux)
|
// credit for this suggestion goes to [Sébastien Chopin](https://github.com/Atinux)
|
||||||
ensuredPush($options, 'created', () => {
|
ensuredPush($options, 'created', () => {
|
||||||
this.$watch('$metaInfo', () => {
|
$this.$watch('$metaInfo', () => {
|
||||||
triggerUpdate($root, 'watcher')
|
triggerUpdate($root, 'watcher')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -88,7 +89,7 @@ export default function createMixin (Vue, options) {
|
|||||||
ensuredPush($options, 'beforeMount', () => {
|
ensuredPush($options, 'beforeMount', () => {
|
||||||
// if this Vue-app was server rendered, set the appId to 'ssr'
|
// if this Vue-app was server rendered, set the appId to 'ssr'
|
||||||
// only one SSR app per page is supported
|
// only one SSR app per page is supported
|
||||||
if ($root.$el && $root.$el.hasAttribute && $root.$el.hasAttribute('data-server-rendered')) {
|
if ($root.$el && $root.$el.nodeType === 1 && $root.$el.hasAttribute('data-server-rendered')) {
|
||||||
$root[rootConfigKey].appId = options.ssrAppId
|
$root[rootConfigKey].appId = options.ssrAppId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -101,7 +102,7 @@ export default function createMixin (Vue, options) {
|
|||||||
$root[rootConfigKey].initializing = true
|
$root[rootConfigKey].initializing = true
|
||||||
|
|
||||||
// refresh meta in nextTick so all child components have loaded
|
// refresh meta in nextTick so all child components have loaded
|
||||||
this.$nextTick(function () {
|
$nextTick(function () {
|
||||||
const { tags, metaInfo } = $root.$meta().refresh()
|
const { tags, metaInfo } = $root.$meta().refresh()
|
||||||
|
|
||||||
// After ssr hydration (identifier by tags === false) check
|
// After ssr hydration (identifier by tags === false) check
|
||||||
@@ -111,7 +112,7 @@ export default function createMixin (Vue, options) {
|
|||||||
// current hook was called
|
// current hook was called
|
||||||
// (during initialization all changes are blocked)
|
// (during initialization all changes are blocked)
|
||||||
if (tags === false && $root[rootConfigKey].initialized === null) {
|
if (tags === false && $root[rootConfigKey].initialized === null) {
|
||||||
this.$nextTick(() => triggerUpdate($root, 'initializing'))
|
$nextTick(() => triggerUpdate($root, 'init'))
|
||||||
}
|
}
|
||||||
|
|
||||||
$root[rootConfigKey].initialized = true
|
$root[rootConfigKey].initialized = true
|
||||||
@@ -145,23 +146,24 @@ export default function createMixin (Vue, options) {
|
|||||||
},
|
},
|
||||||
// TODO: move back into beforeCreate when Vue issue is resolved
|
// TODO: move back into beforeCreate when Vue issue is resolved
|
||||||
destroyed () {
|
destroyed () {
|
||||||
|
const $this = this
|
||||||
// do not trigger refresh:
|
// do not trigger refresh:
|
||||||
// - when the component doesnt have a parent
|
// - when the component doesnt have a parent
|
||||||
// - doesnt have metaInfo defined
|
// - doesnt have metaInfo defined
|
||||||
if (!this.$parent || !hasMetaInfo(this)) {
|
if (!$this.$parent || !hasMetaInfo($this)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait that element is hidden before refreshing meta tags (to support animations)
|
// Wait that element is hidden before refreshing meta tags (to support animations)
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
if (this.$el && this.$el.offsetParent !== null) {
|
if ($this.$el && $this.$el.offsetParent !== null) {
|
||||||
/* istanbul ignore next line */
|
/* istanbul ignore next line */
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
|
|
||||||
triggerUpdate(this.$root, 'destroyed')
|
triggerUpdate($this.$root, 'destroyed')
|
||||||
}, 50)
|
}, 50)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
import { isFunction } from '../utils/is-type'
|
import { isFunction } from '../utils/is-type'
|
||||||
import { rootConfigKey } from './constants'
|
import { rootConfigKey } from './constants'
|
||||||
|
import { pause, resume } from './pausing'
|
||||||
|
|
||||||
export function addNavGuards (rootVm) {
|
export function addNavGuards (rootVm) {
|
||||||
|
const router = rootVm.$router
|
||||||
|
|
||||||
// return when nav guards already added or no router exists
|
// return when nav guards already added or no router exists
|
||||||
if (rootVm[rootConfigKey].navGuards || !rootVm.$router) {
|
if (rootVm[rootConfigKey].navGuards || !router) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rootVm[rootConfigKey].navGuards = true
|
rootVm[rootConfigKey].navGuards = true
|
||||||
|
|
||||||
const $router = rootVm.$router
|
router.beforeEach((to, from, next) => {
|
||||||
const $meta = rootVm.$meta()
|
pause(rootVm)
|
||||||
|
|
||||||
$router.beforeEach((to, from, next) => {
|
|
||||||
$meta.pause()
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
$router.afterEach(() => {
|
router.afterEach(() => {
|
||||||
const { metaInfo } = $meta.resume()
|
const { metaInfo } = resume(rootVm)
|
||||||
if (metaInfo && metaInfo.afterNavigation && isFunction(metaInfo.afterNavigation)) {
|
|
||||||
|
if (metaInfo && isFunction(metaInfo.afterNavigation)) {
|
||||||
metaInfo.afterNavigation(metaInfo)
|
metaInfo.afterNavigation(metaInfo)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
+15
-6
@@ -5,13 +5,22 @@ export function setOptions (options) {
|
|||||||
// combine options
|
// combine options
|
||||||
options = isObject(options) ? options : {}
|
options = isObject(options) ? options : {}
|
||||||
|
|
||||||
for (const key in defaultOptions) {
|
// The options are set like this so they can
|
||||||
if (!options[key]) {
|
// be minified by terser while keeping the
|
||||||
options[key] = defaultOptions[key]
|
// user api intact
|
||||||
}
|
// terser --mangle-properties keep_quoted=strict
|
||||||
|
/* eslint-disable dot-notation */
|
||||||
|
return {
|
||||||
|
keyName: options['keyName'] || defaultOptions.keyName,
|
||||||
|
attribute: options['attribute'] || defaultOptions.attribute,
|
||||||
|
ssrAttribute: options['ssrAttribute'] || defaultOptions.ssrAttribute,
|
||||||
|
tagIDKeyName: options['tagIDKeyName'] || defaultOptions.tagIDKeyName,
|
||||||
|
contentKeyName: options['contentKeyName'] || defaultOptions.contentKeyName,
|
||||||
|
metaTemplateKeyName: options['metaTemplateKeyName'] || defaultOptions.metaTemplateKeyName,
|
||||||
|
ssrAppId: options['ssrAppId'] || defaultOptions.ssrAppId,
|
||||||
|
refreshOnceOnNavigation: !!options['refreshOnceOnNavigation']
|
||||||
}
|
}
|
||||||
|
/* eslint-enable dot-notation */
|
||||||
return options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOptions (options) {
|
export function getOptions (options) {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { rootConfigKey } from './constants'
|
import { rootConfigKey } from './constants'
|
||||||
|
|
||||||
export function pause (rootVm, refresh = true) {
|
export function pause (rootVm, refresh) {
|
||||||
rootVm[rootConfigKey].paused = true
|
rootVm[rootConfigKey].pausing = true
|
||||||
|
|
||||||
return () => resume(refresh)
|
return () => resume(rootVm, refresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resume (rootVm, refresh = true) {
|
export function resume (rootVm, refresh) {
|
||||||
rootVm[rootConfigKey].paused = false
|
rootVm[rootConfigKey].pausing = false
|
||||||
|
|
||||||
if (refresh) {
|
if (refresh || refresh === undefined) {
|
||||||
return rootVm.$meta().refresh()
|
return rootVm.$meta().refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,7 @@ export function applyTemplate ({ component, metaTemplateKeyName, contentKeyName
|
|||||||
// return early if no template defined
|
// return early if no template defined
|
||||||
if (!template) {
|
if (!template) {
|
||||||
// cleanup faulty template properties
|
// cleanup faulty template properties
|
||||||
if (headObject.hasOwnProperty(metaTemplateKeyName)) {
|
delete headObject[metaTemplateKeyName]
|
||||||
delete headObject[metaTemplateKeyName]
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -11,17 +11,17 @@
|
|||||||
// which means the polyfills are removed for other build formats
|
// which means the polyfills are removed for other build formats
|
||||||
const polyfill = process.env.NODE_ENV === 'test'
|
const polyfill = process.env.NODE_ENV === 'test'
|
||||||
|
|
||||||
export function findIndex (array, predicate) {
|
export function findIndex (array, predicate, thisArg) {
|
||||||
if (polyfill && !Array.prototype.findIndex) {
|
if (polyfill && !Array.prototype.findIndex) {
|
||||||
// idx needs to be a Number, for..in returns string
|
// idx needs to be a Number, for..in returns string
|
||||||
for (let idx = 0; idx < array.length; idx++) {
|
for (let idx = 0; idx < array.length; idx++) {
|
||||||
if (predicate.call(arguments[2], array[idx], idx, array)) {
|
if (predicate.call(thisArg, array[idx], idx, array)) {
|
||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return array.findIndex(predicate, arguments[2])
|
return array.findIndex(predicate, thisArg)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toArray (arg) {
|
export function toArray (arg) {
|
||||||
|
|||||||
+11
-3
@@ -1,5 +1,7 @@
|
|||||||
import { toArray } from './array'
|
import { toArray } from './array'
|
||||||
|
|
||||||
|
export const querySelector = (arg, el) => (el || document).querySelectorAll(arg)
|
||||||
|
|
||||||
export function getTag (tags, tag) {
|
export function getTag (tags, tag) {
|
||||||
if (!tags[tag]) {
|
if (!tags[tag]) {
|
||||||
tags[tag] = document.getElementsByTagName(tag)[0]
|
tags[tag] = document.getElementsByTagName(tag)[0]
|
||||||
@@ -14,7 +16,9 @@ export function getElementsKey ({ body, pbody }) {
|
|||||||
: (pbody ? 'pbody' : 'head')
|
: (pbody ? 'pbody' : 'head')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryElements (parentNode, { appId, attribute, type, tagIDKeyName }, attributes = {}) {
|
export function queryElements (parentNode, { appId, attribute, type, tagIDKeyName }, attributes) {
|
||||||
|
attributes = attributes || {}
|
||||||
|
|
||||||
const queries = [
|
const queries = [
|
||||||
`${type}[${attribute}="${appId}"]`,
|
`${type}[${attribute}="${appId}"]`,
|
||||||
`${type}[data-${tagIDKeyName}]`
|
`${type}[data-${tagIDKeyName}]`
|
||||||
@@ -27,9 +31,13 @@ export function queryElements (parentNode, { appId, attribute, type, tagIDKeyNam
|
|||||||
return query
|
return query
|
||||||
})
|
})
|
||||||
|
|
||||||
return toArray(parentNode.querySelectorAll(queries.join(', ')))
|
return toArray(querySelector(queries.join(', '), parentNode))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeElementsByAppId ({ attribute }, appId) {
|
export function removeElementsByAppId ({ attribute }, appId) {
|
||||||
toArray(document.querySelectorAll(`[${attribute}="${appId}"]`)).map(el => el.remove())
|
toArray(querySelector(`[${attribute}="${appId}"]`)).map(el => el.remove())
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeAttribute (el, attributeName) {
|
||||||
|
el.removeAttribute(attributeName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ describe('components', () => {
|
|||||||
expect(guards.after).toBeDefined()
|
expect(guards.after).toBeDefined()
|
||||||
|
|
||||||
guards.before(null, null, () => {})
|
guards.before(null, null, () => {})
|
||||||
expect(wrapper.vm.$root._vueMeta.paused).toBe(true)
|
expect(wrapper.vm.$root._vueMeta.pausing).toBe(true)
|
||||||
|
|
||||||
guards.after()
|
guards.after()
|
||||||
expect(afterNavigation).toHaveBeenCalled()
|
expect(afterNavigation).toHaveBeenCalled()
|
||||||
@@ -292,7 +292,7 @@ describe('components', () => {
|
|||||||
expect(guards.after).toBeDefined()
|
expect(guards.after).toBeDefined()
|
||||||
|
|
||||||
guards.before(null, null, () => {})
|
guards.before(null, null, () => {})
|
||||||
expect(wrapper.vm.$root._vueMeta.paused).toBe(true)
|
expect(wrapper.vm.$root._vueMeta.pausing).toBe(true)
|
||||||
|
|
||||||
guards.after()
|
guards.after()
|
||||||
expect(afterNavigation).toHaveBeenCalled()
|
expect(afterNavigation).toHaveBeenCalled()
|
||||||
|
|||||||
@@ -148,13 +148,13 @@ describe('plugin', () => {
|
|||||||
warn.mockRestore()
|
warn.mockRestore()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('updates can be paused and resumed', async () => {
|
test('updates can be pausing and resumed', async () => {
|
||||||
const { batchUpdate: _batchUpdate } = jest.requireActual('../../src/client/update')
|
const { batchUpdate: _batchUpdate } = jest.requireActual('../../src/client/update')
|
||||||
const batchUpdateSpy = batchUpdate.mockImplementation(_batchUpdate)
|
const batchUpdateSpy = batchUpdate.mockImplementation(_batchUpdate)
|
||||||
// because triggerUpdate & batchUpdate reside in the same file we cant mock them both,
|
// because triggerUpdate & batchUpdate reside in the same file we cant mock them both,
|
||||||
// so just recreate the triggerUpdate fn by copying its implementation
|
// so just recreate the triggerUpdate fn by copying its implementation
|
||||||
const triggerUpdateSpy = triggerUpdate.mockImplementation((vm, hookName) => {
|
const triggerUpdateSpy = triggerUpdate.mockImplementation((vm, hookName) => {
|
||||||
if (vm.$root._vueMeta.initialized && !vm.$root._vueMeta.paused) {
|
if (vm.$root._vueMeta.initialized && !vm.$root._vueMeta.pausing) {
|
||||||
// batch potential DOM updates to prevent extraneous re-rendering
|
// batch potential DOM updates to prevent extraneous re-rendering
|
||||||
batchUpdateSpy(() => vm.$meta().refresh())
|
batchUpdateSpy(() => vm.$meta().refresh())
|
||||||
}
|
}
|
||||||
@@ -185,7 +185,7 @@ describe('plugin', () => {
|
|||||||
|
|
||||||
// no batchUpdate on initialization
|
// no batchUpdate on initialization
|
||||||
expect(wrapper.vm.$root._vueMeta.initialized).toBe(false)
|
expect(wrapper.vm.$root._vueMeta.initialized).toBe(false)
|
||||||
expect(wrapper.vm.$root._vueMeta.paused).toBeFalsy()
|
expect(wrapper.vm.$root._vueMeta.pausing).toBeFalsy()
|
||||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||||
expect(batchUpdateSpy).not.toHaveBeenCalled()
|
expect(batchUpdateSpy).not.toHaveBeenCalled()
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
@@ -196,7 +196,7 @@ describe('plugin', () => {
|
|||||||
|
|
||||||
// batchUpdate on normal update
|
// batchUpdate on normal update
|
||||||
expect(wrapper.vm.$root._vueMeta.initialized).toBe(true)
|
expect(wrapper.vm.$root._vueMeta.initialized).toBe(true)
|
||||||
expect(wrapper.vm.$root._vueMeta.paused).toBeFalsy()
|
expect(wrapper.vm.$root._vueMeta.pausing).toBeFalsy()
|
||||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||||
expect(batchUpdateSpy).toHaveBeenCalledTimes(1)
|
expect(batchUpdateSpy).toHaveBeenCalledTimes(1)
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
@@ -205,9 +205,9 @@ describe('plugin', () => {
|
|||||||
title = 'third title'
|
title = 'third title'
|
||||||
wrapper.setProps({ title })
|
wrapper.setProps({ title })
|
||||||
|
|
||||||
// no batchUpdate when paused
|
// no batchUpdate when pausing
|
||||||
expect(wrapper.vm.$root._vueMeta.initialized).toBe(true)
|
expect(wrapper.vm.$root._vueMeta.initialized).toBe(true)
|
||||||
expect(wrapper.vm.$root._vueMeta.paused).toBe(true)
|
expect(wrapper.vm.$root._vueMeta.pausing).toBe(true)
|
||||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||||
expect(batchUpdateSpy).not.toHaveBeenCalled()
|
expect(batchUpdateSpy).not.toHaveBeenCalled()
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
@@ -225,7 +225,7 @@ describe('plugin', () => {
|
|||||||
// because triggerUpdate & batchUpdate reside in the same file we cant mock them both,
|
// because triggerUpdate & batchUpdate reside in the same file we cant mock them both,
|
||||||
// so just recreate the triggerUpdate fn by copying its implementation
|
// so just recreate the triggerUpdate fn by copying its implementation
|
||||||
triggerUpdate.mockImplementation((vm, hookName) => {
|
triggerUpdate.mockImplementation((vm, hookName) => {
|
||||||
if (vm.$root._vueMeta.initialized && !vm.$root._vueMeta.paused) {
|
if (vm.$root._vueMeta.initialized && !vm.$root._vueMeta.pausing) {
|
||||||
// batch potential DOM updates to prevent extraneous re-rendering
|
// batch potential DOM updates to prevent extraneous re-rendering
|
||||||
batchUpdateSpy(refreshSpy)
|
batchUpdateSpy(refreshSpy)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ describe('updaters', () => {
|
|||||||
add: (tags) => {
|
add: (tags) => {
|
||||||
typeTests.add.expect.forEach((expected, index) => {
|
typeTests.add.expect.forEach((expected, index) => {
|
||||||
if (!['title', 'htmlAttrs', 'headAttrs', 'bodyAttrs'].includes(type)) {
|
if (!['title', 'htmlAttrs', 'headAttrs', 'bodyAttrs'].includes(type)) {
|
||||||
expect(tags.addedTags[type][index].outerHTML).toBe(expected)
|
expect(tags.tagsAdded[type][index].outerHTML).toBe(expected)
|
||||||
}
|
}
|
||||||
expect(html.outerHTML).toContain(expected)
|
expect(html.outerHTML).toContain(expected)
|
||||||
})
|
})
|
||||||
@@ -37,7 +37,7 @@ describe('updaters', () => {
|
|||||||
|
|
||||||
typeTests.change.expect.forEach((expected, index) => {
|
typeTests.change.expect.forEach((expected, index) => {
|
||||||
if (!['title', 'htmlAttrs', 'headAttrs', 'bodyAttrs'].includes(type)) {
|
if (!['title', 'htmlAttrs', 'headAttrs', 'bodyAttrs'].includes(type)) {
|
||||||
expect(tags.addedTags[type][index].outerHTML).toBe(expected)
|
expect(tags.tagsAdded[type][index].outerHTML).toBe(expected)
|
||||||
}
|
}
|
||||||
expect(html.outerHTML).toContain(expected)
|
expect(html.outerHTML).toContain(expected)
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ export const useDist = stdEnv.test && stdEnv.ci
|
|||||||
|
|
||||||
export function getVueMetaPath (browser) {
|
export function getVueMetaPath (browser) {
|
||||||
if (useDist) {
|
if (useDist) {
|
||||||
return path.resolve(__dirname, `../..${browser ? '/dist/vue-meta.js' : ''}`)
|
return path.resolve(__dirname, `../..${browser ? '/dist/vue-meta.min.js' : ''}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
process.server = !browser
|
process.server = !browser
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ const metaInfoData = {
|
|||||||
return () => {
|
return () => {
|
||||||
const tags = defaultTest()
|
const tags = defaultTest()
|
||||||
|
|
||||||
expect(tags.addedTags.meta.length).toBe(1)
|
expect(tags.tagsAdded.meta.length).toBe(1)
|
||||||
// TODO: not sure if we really expect this
|
// TODO: not sure if we really expect this
|
||||||
expect(tags.removedTags.meta.length).toBe(1)
|
expect(tags.tagsRemoved.meta.length).toBe(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,9 +143,9 @@ const metaInfoData = {
|
|||||||
}
|
}
|
||||||
const tags = defaultTest()
|
const tags = defaultTest()
|
||||||
|
|
||||||
expect(tags.addedTags.script[0].parentNode.tagName).toBe('HEAD')
|
expect(tags.tagsAdded.script[0].parentNode.tagName).toBe('HEAD')
|
||||||
expect(tags.addedTags.script[1].parentNode.tagName).toBe('BODY')
|
expect(tags.tagsAdded.script[1].parentNode.tagName).toBe('BODY')
|
||||||
expect(tags.addedTags.script[2].parentNode.tagName).toBe('BODY')
|
expect(tags.tagsAdded.script[2].parentNode.tagName).toBe('BODY')
|
||||||
} else {
|
} else {
|
||||||
// ssr doesnt generate data-body tags
|
// ssr doesnt generate data-body tags
|
||||||
const bodyPrepended = this.expect[1]
|
const bodyPrepended = this.expect[1]
|
||||||
|
|||||||
Reference in New Issue
Block a user