diff --git a/examples/ssr/App.js b/examples/ssr/App.js
index ae55f94..3ac118d 100644
--- a/examples/ssr/App.js
+++ b/examples/ssr/App.js
@@ -138,5 +138,10 @@ export default function createApp () {
`
})
+ const { set } = app.$meta().addApp('custom')
+ set({
+ meta: [{ charset: 'utf-8' }]
+ })
+
return { app, router }
}
diff --git a/examples/ssr/app.template.html b/examples/ssr/app.template.html
index 396a873..10a996c 100644
--- a/examples/ssr/app.template.html
+++ b/examples/ssr/app.template.html
@@ -1,23 +1,16 @@
- {{ title.text() }}
- {{ meta.text() }}
+ {{ head(true) }}
vue-router
@@ -86,7 +79,17 @@ const App = {
const app = new Vue(App)
+const { set, remove } = app.$meta().addApp('custom')
+
+set({
+ meta: [
+ { charset: 'utf=8' }
+ ]
+})
+setTimeout(() => remove(), 3000)
+
app.$mount('#app')
+
/*
const waitFor = time => new Promise(r => setTimeout(r, time || 1000))
const o = {
diff --git a/scripts/rollup.config.js b/scripts/rollup.config.js
index d735e5b..3c00656 100644
--- a/scripts/rollup.config.js
+++ b/scripts/rollup.config.js
@@ -15,7 +15,8 @@ const banner = `/**
* (c) ${new Date().getFullYear()}
* - Declan de Wet
* - Sébastien Chopin (@Atinux)
- * - All the amazing contributors
+ * - Pim (@pimlie)
+ * - All the amazing contributors
* @license MIT
*/
`
@@ -39,13 +40,16 @@ function rollupConfig({
...config
}) {
+ const isBrowserBuild = !config.output || !config.output.format || config.output.format === 'umd' || config.output.file.includes('.browser.')
+
const replaceConfig = {
exclude: 'node_modules/**',
delimiters: ['', ''],
values: {
// replaceConfig needs to have some values
'const polyfill = process.env.NODE_ENV === \'test\'': 'const polyfill = true',
- 'process.env.VERSION': `"${version}"`
+ 'process.env.VERSION': `"${version}"`,
+ 'process.server' : isBrowserBuild ? 'false' : 'true'
}
}
@@ -57,7 +61,7 @@ function rollupConfig({
}*/
return defaultsDeep({}, config, {
- input: 'src/browser.js',
+ input: 'src/index.js',
output: {
name: 'VueMeta',
format: 'umd',
@@ -92,7 +96,6 @@ export default [
},
// common js build
{
- input: 'src/index.js',
output: {
file: pkg.main,
format: 'cjs'
@@ -101,7 +104,6 @@ export default [
},
// esm build
{
- input: 'src/index.js',
output: {
file: pkg.web.replace('.js', '.esm.js'),
format: 'es'
@@ -110,7 +112,6 @@ export default [
},
// browser esm build
{
- input: 'src/browser.js',
output: {
file: pkg.web.replace('.js', '.esm.browser.js'),
format: 'es'
@@ -119,7 +120,6 @@ export default [
},
// minimized browser esm build
{
- input: 'src/browser.js',
output: {
file: pkg.web.replace('.js', '.esm.browser.min.js'),
format: 'es'
diff --git a/src/browser.js b/src/browser.js
deleted file mode 100644
index 87f2c1f..0000000
--- a/src/browser.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import { version } from '../package.json'
-import createMixin from './shared/mixin'
-import { setOptions } from './shared/options'
-import { isUndefined } from './utils/is-type'
-import $meta from './client/$meta'
-import { hasMetaInfo } from './shared/meta-helpers'
-
-/**
- * Plugin install function.
- * @param {Function} Vue - the Vue constructor.
- */
-function install (Vue, options = {}) {
- if (Vue.__vuemeta_installed) {
- return
- }
- Vue.__vuemeta_installed = true
-
- options = setOptions(options)
-
- Vue.prototype.$meta = function () {
- return $meta.call(this, options)
- }
-
- Vue.mixin(createMixin(Vue, options))
-}
-
-// automatic install
-if (!isUndefined(window) && !isUndefined(window.Vue)) {
- /* istanbul ignore next */
- install(window.Vue)
-}
-
-export default {
- version,
- install,
- hasMetaInfo
-}
diff --git a/src/client/$meta.js b/src/client/$meta.js
deleted file mode 100644
index f5d38c5..0000000
--- a/src/client/$meta.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { showWarningNotSupported } from '../shared/log'
-import { getOptions } from '../shared/options'
-import { pause, resume } from '../shared/pausing'
-import refresh from './refresh'
-
-export default function $meta (options = {}) {
- /**
- * Returns an injector for server-side rendering.
- * @this {Object} - the Vue instance (a root component)
- * @return {Object} - injector
- */
- if (!this.$root._vueMeta) {
- return {
- getOptions: showWarningNotSupported,
- refresh: showWarningNotSupported,
- inject: showWarningNotSupported,
- pause: showWarningNotSupported,
- resume: showWarningNotSupported
- }
- }
-
- return {
- getOptions: () => getOptions(options),
- refresh: () => refresh.call(this, options),
- inject: () => {},
- pause: () => pause.call(this),
- resume: () => resume.call(this)
- }
-}
diff --git a/src/client/refresh.js b/src/client/refresh.js
index 8f1ce0b..f4885ae 100644
--- a/src/client/refresh.js
+++ b/src/client/refresh.js
@@ -1,26 +1,34 @@
import { clientSequences } from '../shared/escaping'
+import { showWarningNotSupported } from '../shared/log'
import { getComponentMetaInfo } from '../shared/getComponentOption'
+import { getAppsMetaInfo, clearAppsMetaInfo } from '../shared/additional-app'
import getMetaInfo from '../shared/getMetaInfo'
import { isFunction } from '../utils/is-type'
import updateClientMetaInfo from './updateClientMetaInfo'
-export default function refresh (options = {}) {
- /**
- * When called, will update the current meta info with new meta info.
- * Useful when updating meta info as the result of an asynchronous
- * action that resolves after the initial render takes place.
- *
- * Credit to [Sébastien Chopin](https://github.com/Atinux) for the suggestion
- * to implement this method.
- *
- * @return {Object} - new meta info
- */
+/**
+ * When called, will update the current meta info with new meta info.
+ * Useful when updating meta info as the result of an asynchronous
+ * action that resolves after the initial render takes place.
+ *
+ * Credit to [Sébastien Chopin](https://github.com/Atinux) for the suggestion
+ * to implement this method.
+ *
+ * @return {Object} - new meta info
+ */
+export default function refresh (vm, options = {}) {
+ // make sure vue-meta was initiated
+ if (!vm.$root._vueMeta) {
+ showWarningNotSupported()
+ return {}
+ }
+
// collect & aggregate all metaInfo $options
- const rawInfo = getComponentMetaInfo(options, this.$root)
+ const rawInfo = getComponentMetaInfo(options, vm.$root)
- const metaInfo = getMetaInfo(options, rawInfo, clientSequences, this.$root)
+ const metaInfo = getMetaInfo(options, rawInfo, clientSequences, vm.$root)
- const appId = this.$root._vueMeta.appId
+ const { appId } = vm.$root._vueMeta
const tags = updateClientMetaInfo(appId, options, metaInfo)
// emit "event" with new info
@@ -28,5 +36,14 @@ export default function refresh (options = {}) {
metaInfo.changed(metaInfo, tags.addedTags, tags.removedTags)
}
- return { vm: this, metaInfo, tags }
+ const appsMetaInfo = getAppsMetaInfo()
+ if (appsMetaInfo) {
+ for (const additionalAppId in appsMetaInfo) {
+ updateClientMetaInfo(additionalAppId, options, appsMetaInfo[additionalAppId])
+ delete appsMetaInfo[additionalAppId]
+ }
+ clearAppsMetaInfo(true)
+ }
+
+ return { vm, metaInfo, tags }
}
diff --git a/src/index.js b/src/index.js
index 7cc601f..0ce148c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,7 @@
import { version } from '../package.json'
import createMixin from './shared/mixin'
import { setOptions } from './shared/options'
-import $meta from './server/$meta'
+import $meta from './shared/$meta'
import generate from './server/generate'
import { hasMetaInfo } from './shared/meta-helpers'
@@ -27,6 +27,6 @@ function install (Vue, options = {}) {
export default {
version,
install,
- hasMetaInfo,
- generate
+ generate: process.server ? generate : () => {},
+ hasMetaInfo
}
diff --git a/src/server/$meta.js b/src/server/$meta.js
deleted file mode 100644
index 76e040a..0000000
--- a/src/server/$meta.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { getOptions } from '../shared/options'
-import { pause, resume } from '../shared/pausing'
-import refresh from '../client/refresh'
-import inject from './inject'
-
-export default function $meta (options = {}) {
- /**
- * Returns an injector for server-side rendering.
- * @this {Object} - the Vue instance (a root component)
- * @return {Object} - injector
- */
- return {
- getOptions: () => getOptions(options),
- refresh: () => refresh.call(this, options),
- inject: () => inject.call(this, options),
- pause: () => pause.call(this),
- resume: () => resume.call(this)
- }
-}
diff --git a/src/server/generate.js b/src/server/generate.js
index f3210f1..90b4027 100644
--- a/src/server/generate.js
+++ b/src/server/generate.js
@@ -5,5 +5,7 @@ import generateServerInjector from './generateServerInjector'
export default function generate (rawInfo, options = {}) {
const metaInfo = getMetaInfo(setOptions(options), rawInfo, serverSequences)
- return generateServerInjector(options, metaInfo)
+
+ const serverInjector = generateServerInjector(options, metaInfo)
+ return serverInjector.injectors
}
diff --git a/src/server/generateServerInjector.js b/src/server/generateServerInjector.js
index 25d3fcb..2adbca8 100644
--- a/src/server/generateServerInjector.js
+++ b/src/server/generateServerInjector.js
@@ -9,24 +9,71 @@ import { titleGenerator, attributeGenerator, tagGenerator } from './generators'
* @return {Object} - the new injector
*/
-export default function generateServerInjector (options, newInfo) {
+export default function generateServerInjector (options, metaInfo) {
+ const serverInjector = {
+ data: metaInfo,
+ extraData: undefined,
+ addInfo (appId, metaInfo) {
+ this.extraData = this.extraData || {}
+ this.extraData[appId] = metaInfo
+ },
+ callInjectors (opts) {
+ const m = this.injectors
+
+ // only call title for the head
+ return (opts.body || opts.pbody ? '' : m.title.text(opts)) +
+ m.meta.text(opts) +
+ m.link.text(opts) +
+ m.style.text(opts) +
+ m.script.text(opts) +
+ m.noscript.text(opts)
+ },
+ injectors: {
+ head: ln => serverInjector.callInjectors({ ln }),
+ bodyPrepend: ln => serverInjector.callInjectors({ ln, pbody: true }),
+ bodyAppend: ln => serverInjector.callInjectors({ ln, body: true })
+ }
+ }
+
for (const type in defaultInfo) {
if (metaInfoOptionKeys.includes(type)) {
continue
}
- if (type === 'title') {
- newInfo[type] = titleGenerator(options, type, newInfo[type])
- continue
- }
+ serverInjector.injectors[type] = {
+ text (arg) {
+ if (type === 'title') {
+ return titleGenerator(options, type, serverInjector.data[type], arg)
+ }
- if (metaInfoAttributeKeys.includes(type)) {
- newInfo[type] = attributeGenerator(options, type, newInfo[type])
- continue
- }
+ if (metaInfoAttributeKeys.includes(type)) {
+ let str = attributeGenerator(options, type, serverInjector.data[type], arg)
- newInfo[type] = tagGenerator(options, type, newInfo[type])
+ if (serverInjector.extraData) {
+ for (const appId in serverInjector.extraData) {
+ const data = serverInjector.extraData[appId][type]
+ const extraStr = attributeGenerator(options, type, data, arg)
+ str = `${str}${extraStr}`
+ }
+ }
+
+ return str
+ }
+
+ let str = tagGenerator(options, type, serverInjector.data[type], arg)
+
+ if (serverInjector.extraData) {
+ for (const appId in serverInjector.extraData) {
+ const data = serverInjector.extraData[appId][type]
+ const extraStr = tagGenerator(options, type, data, { appId, ...arg })
+ str = `${str}${extraStr}`
+ }
+ }
+
+ return str
+ }
+ }
}
- return newInfo
+ return serverInjector
}
diff --git a/src/server/generators/attribute.js b/src/server/generators/attribute.js
index ebfc6aa..a79049a 100644
--- a/src/server/generators/attribute.js
+++ b/src/server/generators/attribute.js
@@ -8,33 +8,29 @@ import { isUndefined, isArray } from '../../utils/is-type'
* @param {Object} data - the attributes to generate
* @return {Object} - the attribute generator
*/
-export default function attributeGenerator ({ attribute, ssrAttribute } = {}, type, data) {
- return {
- text (addSrrAttribute) {
- let attributeStr = ''
- const watchedAttrs = []
+export default function attributeGenerator ({ attribute, ssrAttribute } = {}, type, data, addSrrAttribute) {
+ let attributeStr = ''
+ const watchedAttrs = []
- for (const attr in data) {
- if (data.hasOwnProperty(attr)) {
- watchedAttrs.push(attr)
+ for (const attr in data) {
+ if (data.hasOwnProperty(attr)) {
+ watchedAttrs.push(attr)
- attributeStr += isUndefined(data[attr]) || booleanHtmlAttributes.includes(attr)
- ? attr
- : `${attr}="${isArray(data[attr]) ? data[attr].join(' ') : data[attr]}"`
+ attributeStr += isUndefined(data[attr]) || booleanHtmlAttributes.includes(attr)
+ ? attr
+ : `${attr}="${isArray(data[attr]) ? data[attr].join(' ') : data[attr]}"`
- attributeStr += ' '
- }
- }
-
- if (attributeStr) {
- attributeStr += `${attribute}="${(watchedAttrs.sort()).join(',')}"`
- }
-
- if (type === 'htmlAttrs' && addSrrAttribute) {
- return `${ssrAttribute}${attributeStr ? ' ' : ''}${attributeStr}`
- }
-
- return attributeStr
+ attributeStr += ' '
}
}
+
+ if (attributeStr) {
+ attributeStr += `${attribute}="${(watchedAttrs.sort()).join(',')}"`
+ }
+
+ if (type === 'htmlAttrs' && addSrrAttribute) {
+ return `${ssrAttribute}${attributeStr ? ' ' : ''}${attributeStr}`
+ }
+
+ return attributeStr
}
diff --git a/src/server/generators/tag.js b/src/server/generators/tag.js
index a5c6245..e4ed12d 100644
--- a/src/server/generators/tag.js
+++ b/src/server/generators/tag.js
@@ -14,79 +14,76 @@ import {
* @param {(Array