2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-07 17:52:24 +03:00

chore(release): 3.0.0-alpha.2

This commit is contained in:
pimlie
2021-02-28 23:30:04 +01:00
parent d82508a6d3
commit 3c2d9ce284
10 changed files with 863 additions and 439 deletions
+7
View File
@@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [3.0.0-alpha.2](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) (2021-02-28)
### Features
* add support for computed metadata ([3e1a0da](https://github.com/nuxt/vue-meta/commit/3e1a0da9e4d744f74702ae11bbe3a1bec0f0a125))
## [3.0.0-alpha.1](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.0...v3.0.0-alpha.1) (2021-01-31)
+154 -77
View File
@@ -1,5 +1,5 @@
/**
* vue-meta v3.0.0-alpha.1
* vue-meta v3.0.0-alpha.2
* (c) 2021
* - Pim (@pimlie)
* - All the amazing contributors
@@ -612,7 +612,7 @@ function renderAttributes(context, key, data, config) {
}
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
const slot = slots && slots[slotName];
if (!slot) {
if (!slot || !isFunction(slot)) {
return content;
}
const slotScopeProps = {
@@ -635,9 +635,34 @@ const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag ===
const PolySymbol = (name) =>
// vm = vue meta
hasSymbol
? Symbol( '[vue-meta]: ' + name )
: ( '[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
? Symbol('[vue-meta]: ' + name )
: ('[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol('meta_active' );
/**
* Apply the differences between newSource & oldSource to target
*/
function applyDifference(target, newSource, oldSource) {
for (const key in newSource) {
if (!(key in oldSource)) {
target[key] = newSource[key];
continue;
}
// We dont care about nested objects here , these changes
// should already have been tracked by the MergeProxy
if (isObject(target[key])) {
continue;
}
if (newSource[key] !== oldSource[key]) {
target[key] = newSource[key];
}
}
for (const key in oldSource) {
if (!(key in newSource)) {
delete target[key];
}
}
}
function getCurrentManager(vm) {
if (!vm) {
@@ -649,15 +674,22 @@ function getCurrentManager(vm) {
return vm.appContext.config.globalProperties.$metaManager;
}
function useMeta(source, manager) {
const vm = vue.getCurrentInstance();
const vm = vue.getCurrentInstance() || undefined;
if (!manager && vm) {
manager = getCurrentManager(vm);
}
if (!manager) {
// oopsydoopsy
throw new Error('No manager or current instance');
}
return manager.addMeta(source, vm || undefined);
if (vue.isProxy(source)) {
vue.watch(source, (newSource, oldSource) => {
// We only care about first level props, second+ level will already be changed by the merge proxy
applyDifference(metaProxy.meta, newSource, oldSource);
});
source = source.value;
}
const metaProxy = manager.addMeta(source, vm);
return metaProxy;
}
function useActiveMeta() {
return vue.inject(metaActiveKey);
@@ -695,83 +727,128 @@ function addVnode(teleports, to, vnodes) {
}
teleports[to].push(...nodes);
}
function createMetaManager(config, resolver) {
const createMetaManager = (config, resolver) => MetaManager.create(config, resolver);
class MetaManager {
constructor(config, target, resolver) {
this.ssrCleanedUp = false;
this.config = config;
this.target = target;
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
this.resolver = resolver;
}
}
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = this;
app.provide(metaActiveKey, active);
}
addMeta(metadata, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const metaGuards = ({
removed: []
});
const resolveContext = { vm };
if (this.resolver) {
this.resolver.setup(resolveContext);
}
// TODO: optimize initial compute (once)
const meta = this.target.addSource(metadata, resolveContext, true);
const onRemoved = (removeGuard) => metaGuards.removed.push(removeGuard);
const unmount = (ignoreGuards) => this.unmount(!!ignoreGuards, meta, metaGuards, vm);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
onRemoved,
unmount
};
}
unmount(ignoreGuards, meta, metaGuards, vm) {
if (vm) {
const { $el } = vm.proxy;
// Wait for element to be removed from DOM
if ($el && $el.offsetParent) {
let observer = new MutationObserver((records) => {
for (const { removedNodes } of records) {
if (!removedNodes) {
continue;
}
removedNodes.forEach((el) => {
if (el === $el && observer) {
observer.disconnect();
observer = undefined;
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
});
}
});
observer.observe($el.parentNode, { childList: true });
return;
}
}
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
async reallyUnmount(ignoreGuards, meta, metaGuards) {
this.target.delSource(meta);
if (!ignoreGuards && metaGuards) {
await Promise.all(metaGuards.removed.map(removeGuard => removeGuard()));
}
}
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
}
MetaManager.create = (config, resolver) => {
const resolve = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments);
}
return resolver.resolve(options, contexts, active, key, pathSegments);
};
const { addSource, delSource } = createMergedObject(resolve, active);
const mergedObject = createMergedObject(resolve, active);
// TODO: validate resolver
const manager = {
config,
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager;
app.provide(metaActiveKey, active);
},
addMeta(metaObj, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext);
}
// TODO: optimize initial compute
const meta = addSource(metaObj, resolveContext, true);
const unmount = () => delSource(meta);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
unmount
};
},
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
};
const manager = new MetaManager(config, mergedObject, resolver);
return manager;
}
};
// rollup doesnt like an import as it cant find the export so use require
const { renderToString } = require('@vue/server-renderer');
+154 -77
View File
@@ -1,5 +1,5 @@
/**
* vue-meta v3.0.0-alpha.1
* vue-meta v3.0.0-alpha.2
* (c) 2021
* - Pim (@pimlie)
* - All the amazing contributors
@@ -608,7 +608,7 @@ function renderAttributes(context, key, data, config) {
}
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
const slot = slots && slots[slotName];
if (!slot) {
if (!slot || !isFunction(slot)) {
return content;
}
const slotScopeProps = {
@@ -631,9 +631,34 @@ const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag ===
const PolySymbol = (name) =>
// vm = vue meta
hasSymbol
? Symbol( name)
: ( '_vm_') + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol( 'am');
? Symbol(name)
: ('_vm_') + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol('ma');
/**
* Apply the differences between newSource & oldSource to target
*/
function applyDifference(target, newSource, oldSource) {
for (const key in newSource) {
if (!(key in oldSource)) {
target[key] = newSource[key];
continue;
}
// We dont care about nested objects here , these changes
// should already have been tracked by the MergeProxy
if (isObject(target[key])) {
continue;
}
if (newSource[key] !== oldSource[key]) {
target[key] = newSource[key];
}
}
for (const key in oldSource) {
if (!(key in newSource)) {
delete target[key];
}
}
}
function getCurrentManager(vm) {
if (!vm) {
@@ -645,15 +670,22 @@ function getCurrentManager(vm) {
return vm.appContext.config.globalProperties.$metaManager;
}
function useMeta(source, manager) {
const vm = vue.getCurrentInstance();
const vm = vue.getCurrentInstance() || undefined;
if (!manager && vm) {
manager = getCurrentManager(vm);
}
if (!manager) {
// oopsydoopsy
throw new Error('No manager or current instance');
}
return manager.addMeta(source, vm || undefined);
if (vue.isProxy(source)) {
vue.watch(source, (newSource, oldSource) => {
// We only care about first level props, second+ level will already be changed by the merge proxy
applyDifference(metaProxy.meta, newSource, oldSource);
});
source = source.value;
}
const metaProxy = manager.addMeta(source, vm);
return metaProxy;
}
function useActiveMeta() {
return vue.inject(metaActiveKey);
@@ -691,83 +723,128 @@ function addVnode(teleports, to, vnodes) {
}
teleports[to].push(...nodes);
}
function createMetaManager(config, resolver) {
const createMetaManager = (config, resolver) => MetaManager.create(config, resolver);
class MetaManager {
constructor(config, target, resolver) {
this.ssrCleanedUp = false;
this.config = config;
this.target = target;
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
this.resolver = resolver;
}
}
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = this;
app.provide(metaActiveKey, active);
}
addMeta(metadata, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const metaGuards = ({
removed: []
});
const resolveContext = { vm };
if (this.resolver) {
this.resolver.setup(resolveContext);
}
// TODO: optimize initial compute (once)
const meta = this.target.addSource(metadata, resolveContext, true);
const onRemoved = (removeGuard) => metaGuards.removed.push(removeGuard);
const unmount = (ignoreGuards) => this.unmount(!!ignoreGuards, meta, metaGuards, vm);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
onRemoved,
unmount
};
}
unmount(ignoreGuards, meta, metaGuards, vm) {
if (vm) {
const { $el } = vm.proxy;
// Wait for element to be removed from DOM
if ($el && $el.offsetParent) {
let observer = new MutationObserver((records) => {
for (const { removedNodes } of records) {
if (!removedNodes) {
continue;
}
removedNodes.forEach((el) => {
if (el === $el && observer) {
observer.disconnect();
observer = undefined;
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
});
}
});
observer.observe($el.parentNode, { childList: true });
return;
}
}
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
async reallyUnmount(ignoreGuards, meta, metaGuards) {
this.target.delSource(meta);
if (!ignoreGuards && metaGuards) {
await Promise.all(metaGuards.removed.map(removeGuard => removeGuard()));
}
}
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
}
MetaManager.create = (config, resolver) => {
const resolve = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments);
}
return resolver.resolve(options, contexts, active, key, pathSegments);
};
const { addSource, delSource } = createMergedObject(resolve, active);
const mergedObject = createMergedObject(resolve, active);
// TODO: validate resolver
const manager = {
config,
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager;
app.provide(metaActiveKey, active);
},
addMeta(metaObj, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext);
}
// TODO: optimize initial compute
const meta = addSource(metaObj, resolveContext, true);
const unmount = () => delSource(meta);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
unmount
};
},
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
};
const manager = new MetaManager(config, mergedObject, resolver);
return manager;
}
};
// rollup doesnt like an import as it cant find the export so use require
const { renderToString } = require('@vue/server-renderer');
+49 -17
View File
@@ -1,13 +1,45 @@
import { ComponentInternalInstance, App, Slots, VNode } from 'vue';
import { App, ComponentInternalInstance, Slots, VNode } from 'vue';
import { SSRContext } from '@vue/server-renderer';
declare type MergeSource = {
[key: string]: any;
};
declare type MergedObjectValue = boolean | number | string | MergedObject | any;
declare type MergedObject = {
[key: string]: MergedObjectValue;
};
declare type PathSegments = Array<string>;
declare type ResolveContext = {};
declare type ResolveMethod = (options: Array<any>, contexts: Array<ResolveContext>, active: MergedObjectValue, key: string | number | symbol, pathSegments: PathSegments) => MergedObjectValue;
declare type ResolveMethod = (options: Array<any>, contexts: Array<ResolveContext>, active: MergedObjectValue, key: string | number | symbol, pathSegments: PathSegments) => MergedObjectValue;
declare type MergeContext = {
resolve: ResolveMethod;
active: MergedObject;
sources: Array<MergeSource>;
};
declare type MergedObjectBuilder = {
context: MergeContext;
compute: () => void;
addSource: (source: MergeSource, resolveContext: ResolveContext | undefined, recompute?: Boolean) => any;
delSource: (sourceOrProxy: MergeSource, recompute?: boolean) => boolean;
};
declare type createMetaManagerMethod = (config: MetaConfig, resolver: MetaResolver | ResolveMethod) => MetaManager;
declare const createMetaManager: createMetaManagerMethod;
declare class MetaManager {
config: MetaConfig;
target: MergedObjectBuilder;
resolver?: MetaResolverSetup;
ssrCleanedUp: boolean;
constructor(config: MetaConfig, target: MergedObjectBuilder, resolver: MetaResolver | ResolveMethod);
static create: createMetaManagerMethod;
install(app: App): void;
addMeta(metadata: MetaSource, vm?: ComponentInternalInstance): MetaProxy;
private unmount;
private reallyUnmount;
render({ slots }?: {
slots?: Slots;
}): VNode[];
}
declare type MetaConfigSectionKey = 'tag' | 'to' | 'keyAttribute' | 'valueAttribute' | 'nameless' | 'group' | 'namespaced' | 'namespacedAttribute' | 'attributesFor';
interface MetaConfigSectionTag {
@@ -40,6 +72,7 @@ declare type MetaTagsConfig = {
[key in MetaTagName]: MetaTagConfig;
};
declare type Modify<T, R> = Omit<T, keyof R> & R;
declare type TODO = any;
/**
* Proxied meta source for tracking changes and updating the active meta daa
@@ -52,12 +85,14 @@ interface MetaSourceProxy extends MergedObject {
declare type MetaSource = {
[key: string]: TODO;
};
declare type MetaGuardRemoved = () => void | Promise<void>;
/**
* Return value of the useMeta api
*/
declare type MetaProxy = {
meta: MetaSourceProxy;
unmount: Function | false;
onRemoved: (removeGuard: MetaGuardRemoved) => void;
unmount: (ignoreGuards?: boolean) => void;
};
/**
* The active/aggregated meta data currently rendered
@@ -76,23 +111,21 @@ declare type MetaResolver = {
setup?: MetaResolveSetup;
resolve: ResolveMethod;
};
/**
* The meta manager
*/
declare type MetaManager = {
readonly config: MetaConfig;
install(app: App): void;
addMeta(source: MetaSource, vm?: ComponentInternalInstance): MetaProxy;
render(ctx?: {
slots?: Slots;
}): Array<VNode>;
};
declare type MetaResolverSetup = Modify<MetaResolver, {
setup: MetaResolveSetup;
}>;
/**
* @internal
*/
declare type MetaTeleports = {
[key: string]: Array<VNode>;
};
/**
* @internal
*/
interface MetaGuards {
removed: Array<MetaGuardRemoved>;
}
/**
* @internal
*/
@@ -132,6 +165,7 @@ declare type MetaRendered = Array<MetaRenderedNode>;
declare module '@vue/runtime-core' {
interface ComponentInternalInstance {
$metaManager: MetaManager;
$metaGuards: MetaGuards;
}
}
@@ -152,8 +186,6 @@ declare namespace deepest_d {
declare const defaultConfig: MetaConfig;
declare function createMetaManager(config: MetaConfig, resolver: MetaResolver | ResolveMethod): MetaManager;
declare type ResolveOptionReducer = (accumulator: any, context: ResolveContext) => ResolveMethod;
declare const resolveOption: (predicament: ResolveOptionReducer) => ResolveMethod;
@@ -163,4 +195,4 @@ declare function getCurrentManager(vm?: ComponentInternalInstance): MetaManager
declare function useMeta(source: MetaSource, manager?: MetaManager): MetaProxy;
declare function useActiveMeta(): MetaActive;
export { MetaActive, MetaConfig, MetaConfigSection, MetaConfigSectionAttribute, MetaConfigSectionGroup, MetaConfigSectionKey, MetaConfigSectionTag, MetaGroupConfig, MetaManager, MetaProxy, MetaRenderContext, MetaRendered, MetaRenderedNode, MetaResolveContext, MetaResolveSetup, MetaResolver, MetaSource, MetaSourceProxy, MetaTagConfig, MetaTagConfigKey, MetaTagName, MetaTagsConfig, MetaTeleports, SlotScopeProperties, TODO, createMetaManager, deepest_d as deepestResolver, defaultConfig, getCurrentManager, renderToStringWithMeta, resolveOption, useActiveMeta, useMeta };
export { MetaActive, MetaConfig, MetaConfigSection, MetaConfigSectionAttribute, MetaConfigSectionGroup, MetaConfigSectionKey, MetaConfigSectionTag, MetaGroupConfig, MetaGuardRemoved, MetaGuards, MetaProxy, MetaRenderContext, MetaRendered, MetaRenderedNode, MetaResolveContext, MetaResolveSetup, MetaResolver, MetaResolverSetup, MetaSource, MetaSourceProxy, MetaTagConfig, MetaTagConfigKey, MetaTagName, MetaTagsConfig, MetaTeleports, Modify, SlotScopeProperties, TODO, createMetaManager, deepest_d as deepestResolver, defaultConfig, getCurrentManager, renderToStringWithMeta, resolveOption, useActiveMeta, useMeta };
+171 -94
View File
@@ -1,12 +1,12 @@
/**
* vue-meta v3.0.0-alpha.1
* vue-meta v3.0.0-alpha.2
* (c) 2021
* - Pim (@pimlie)
* - All the amazing contributors
* @license MIT
*/
import { markRaw, h, getCurrentInstance, inject, defineComponent, reactive, onUnmounted, Teleport, Comment } from 'vue';
import { markRaw, h, getCurrentInstance, isProxy, watch, inject, defineComponent, reactive, onUnmounted, Teleport, Comment } from 'vue';
const resolveOption = predicament => (options, contexts) => {
let resolvedIndex = -1;
@@ -600,12 +600,12 @@ function renderAttributes(context, key, data, config) {
}
if (!cachedElements[attributesFor]) {
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
if ( !el) {
if (!el) {
// eslint-disable-next-line no-console
console.error('Could not find element for selector', attributesFor, ', won\'t render attributes');
return;
}
if ( el2) {
if (el2) {
// eslint-disable-next-line no-console
console.warn('Found multiple elements for selector', attributesFor);
}
@@ -629,7 +629,7 @@ function renderAttributes(context, key, data, config) {
}
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
const slot = slots && slots[slotName];
if (!slot) {
if (!slot || !isFunction(slot)) {
return content;
}
const slotScopeProps = {
@@ -652,9 +652,34 @@ const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag ===
const PolySymbol = (name) =>
// vm = vue meta
hasSymbol
? Symbol( '[vue-meta]: ' + name )
: ( '[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
? Symbol('[vue-meta]: ' + name )
: ('[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol('meta_active' );
/**
* Apply the differences between newSource & oldSource to target
*/
function applyDifference(target, newSource, oldSource) {
for (const key in newSource) {
if (!(key in oldSource)) {
target[key] = newSource[key];
continue;
}
// We dont care about nested objects here , these changes
// should already have been tracked by the MergeProxy
if (isObject(target[key])) {
continue;
}
if (newSource[key] !== oldSource[key]) {
target[key] = newSource[key];
}
}
for (const key in oldSource) {
if (!(key in newSource)) {
delete target[key];
}
}
}
function getCurrentManager(vm) {
if (!vm) {
@@ -666,15 +691,22 @@ function getCurrentManager(vm) {
return vm.appContext.config.globalProperties.$metaManager;
}
function useMeta(source, manager) {
const vm = getCurrentInstance();
const vm = getCurrentInstance() || undefined;
if (!manager && vm) {
manager = getCurrentManager(vm);
}
if (!manager) {
// oopsydoopsy
throw new Error('No manager or current instance');
}
return manager.addMeta(source, vm || undefined);
if (isProxy(source)) {
watch(source, (newSource, oldSource) => {
// We only care about first level props, second+ level will already be changed by the merge proxy
applyDifference(metaProxy.meta, newSource, oldSource);
});
source = source.value;
}
const metaProxy = manager.addMeta(source, vm);
return metaProxy;
}
function useActiveMeta() {
return inject(metaActiveKey);
@@ -713,96 +745,141 @@ function addVnode(teleports, to, vnodes) {
}
teleports[to].push(...nodes);
}
function createMetaManager(config, resolver) {
let cleanedUpSsr = false;
const createMetaManager = (config, resolver) => MetaManager.create(config, resolver);
class MetaManager {
constructor(config, target, resolver) {
this.ssrCleanedUp = false;
this.config = config;
this.target = target;
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
this.resolver = resolver;
}
}
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = this;
app.provide(metaActiveKey, active);
}
addMeta(metadata, vm) {
if (!vm) {
vm = getCurrentInstance() || undefined;
}
const metaGuards = ({
removed: []
});
const resolveContext = { vm };
if (this.resolver) {
this.resolver.setup(resolveContext);
}
// TODO: optimize initial compute (once)
const meta = this.target.addSource(metadata, resolveContext, true);
const onRemoved = (removeGuard) => metaGuards.removed.push(removeGuard);
const unmount = (ignoreGuards) => this.unmount(!!ignoreGuards, meta, metaGuards, vm);
if (vm) {
onUnmounted(unmount);
}
return {
meta,
onRemoved,
unmount
};
}
unmount(ignoreGuards, meta, metaGuards, vm) {
if (vm) {
const { $el } = vm.proxy;
// Wait for element to be removed from DOM
if ($el && $el.offsetParent) {
let observer = new MutationObserver((records) => {
for (const { removedNodes } of records) {
if (!removedNodes) {
continue;
}
removedNodes.forEach((el) => {
if (el === $el && observer) {
observer.disconnect();
observer = undefined;
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
});
}
});
observer.observe($el.parentNode, { childList: true });
return;
}
}
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
async reallyUnmount(ignoreGuards, meta, metaGuards) {
this.target.delSource(meta);
if (!ignoreGuards && metaGuards) {
await Promise.all(metaGuards.removed.map(removeGuard => removeGuard()));
}
}
render({ slots } = {}) {
// TODO: clean this method
// cleanup ssr tags if not yet done
if (!this.ssrCleanedUp) {
this.ssrCleanedUp = true;
// Listen for DOM loaded because tags in the body couldnt
// have loaded yet once the manager does it first render
// (preferable there should only be one meta render on hydration)
window.addEventListener('DOMContentLoaded', () => {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
if (ssrTags && ssrTags.length) {
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el));
}
});
}
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return h(Teleport, { to }, teleports[to]);
});
}
}
MetaManager.create = (config, resolver) => {
const resolve = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments);
}
return resolver.resolve(options, contexts, active, key, pathSegments);
};
const { addSource, delSource } = createMergedObject(resolve, active);
const mergedObject = createMergedObject(resolve, active);
// TODO: validate resolver
const manager = {
config,
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager;
app.provide(metaActiveKey, active);
},
addMeta(metaObj, vm) {
if (!vm) {
vm = getCurrentInstance() || undefined;
}
const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext);
}
// TODO: optimize initial compute
const meta = addSource(metaObj, resolveContext, true);
const unmount = () => delSource(meta);
if (vm) {
onUnmounted(unmount);
}
return {
meta,
unmount
};
},
render({ slots } = {}) {
// cleanup ssr tags if not yet done
if ( !cleanedUpSsr) {
cleanedUpSsr = true;
// Listen for DOM loaded because tags in the body couldnt
// have loaded yet once the manager does it first render
// (preferable there should only be one meta render on hydration)
window.addEventListener('DOMContentLoaded', () => {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
if (ssrTags && ssrTags.length) {
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el));
}
});
}
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return h(Teleport, { to }, teleports[to]);
});
}
};
const manager = new MetaManager(config, mergedObject, resolver);
return manager;
}
};
export { createMetaManager, deepest as deepestResolver, defaultConfig, getCurrentManager, resolveOption, useActiveMeta, useMeta };
+2 -2
View File
File diff suppressed because one or more lines are too long
+153 -76
View File
@@ -1,12 +1,12 @@
/**
* vue-meta v3.0.0-alpha.1
* vue-meta v3.0.0-alpha.2
* (c) 2021
* - Pim (@pimlie)
* - All the amazing contributors
* @license MIT
*/
import { markRaw, h, getCurrentInstance, inject, defineComponent, reactive, onUnmounted, Teleport } from 'vue';
import { markRaw, h, getCurrentInstance, isProxy, watch, inject, defineComponent, reactive, onUnmounted, Teleport } from 'vue';
const resolveOption = predicament => (options, contexts) => {
let resolvedIndex = -1;
@@ -608,7 +608,7 @@ function renderAttributes(context, key, data, config) {
}
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
const slot = slots && slots[slotName];
if (!slot) {
if (!slot || !isFunction(slot)) {
return content;
}
const slotScopeProps = {
@@ -633,7 +633,32 @@ const PolySymbol = (name) =>
hasSymbol
? Symbol((process.env.NODE_ENV !== 'production') ? '[vue-meta]: ' + name : name)
: ((process.env.NODE_ENV !== 'production') ? '[vue-meta]: ' : '_vm_') + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol((process.env.NODE_ENV !== 'production') ? 'active_meta' : 'am');
const metaActiveKey = /*#__PURE__*/ PolySymbol((process.env.NODE_ENV !== 'production') ? 'meta_active' : 'ma');
/**
* Apply the differences between newSource & oldSource to target
*/
function applyDifference(target, newSource, oldSource) {
for (const key in newSource) {
if (!(key in oldSource)) {
target[key] = newSource[key];
continue;
}
// We dont care about nested objects here , these changes
// should already have been tracked by the MergeProxy
if (isObject(target[key])) {
continue;
}
if (newSource[key] !== oldSource[key]) {
target[key] = newSource[key];
}
}
for (const key in oldSource) {
if (!(key in newSource)) {
delete target[key];
}
}
}
function getCurrentManager(vm) {
if (!vm) {
@@ -645,15 +670,22 @@ function getCurrentManager(vm) {
return vm.appContext.config.globalProperties.$metaManager;
}
function useMeta(source, manager) {
const vm = getCurrentInstance();
const vm = getCurrentInstance() || undefined;
if (!manager && vm) {
manager = getCurrentManager(vm);
}
if (!manager) {
// oopsydoopsy
throw new Error('No manager or current instance');
}
return manager.addMeta(source, vm || undefined);
if (isProxy(source)) {
watch(source, (newSource, oldSource) => {
// We only care about first level props, second+ level will already be changed by the merge proxy
applyDifference(metaProxy.meta, newSource, oldSource);
});
source = source.value;
}
const metaProxy = manager.addMeta(source, vm);
return metaProxy;
}
function useActiveMeta() {
return inject(metaActiveKey);
@@ -691,83 +723,128 @@ function addVnode(teleports, to, vnodes) {
}
teleports[to].push(...nodes);
}
function createMetaManager(config, resolver) {
const createMetaManager = (config, resolver) => MetaManager.create(config, resolver);
class MetaManager {
constructor(config, target, resolver) {
this.ssrCleanedUp = false;
this.config = config;
this.target = target;
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
this.resolver = resolver;
}
}
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = this;
app.provide(metaActiveKey, active);
}
addMeta(metadata, vm) {
if (!vm) {
vm = getCurrentInstance() || undefined;
}
const metaGuards = ({
removed: []
});
const resolveContext = { vm };
if (this.resolver) {
this.resolver.setup(resolveContext);
}
// TODO: optimize initial compute (once)
const meta = this.target.addSource(metadata, resolveContext, true);
const onRemoved = (removeGuard) => metaGuards.removed.push(removeGuard);
const unmount = (ignoreGuards) => this.unmount(!!ignoreGuards, meta, metaGuards, vm);
if (vm) {
onUnmounted(unmount);
}
return {
meta,
onRemoved,
unmount
};
}
unmount(ignoreGuards, meta, metaGuards, vm) {
if (vm) {
const { $el } = vm.proxy;
// Wait for element to be removed from DOM
if ($el && $el.offsetParent) {
let observer = new MutationObserver((records) => {
for (const { removedNodes } of records) {
if (!removedNodes) {
continue;
}
removedNodes.forEach((el) => {
if (el === $el && observer) {
observer.disconnect();
observer = undefined;
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
});
}
});
observer.observe($el.parentNode, { childList: true });
return;
}
}
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
async reallyUnmount(ignoreGuards, meta, metaGuards) {
this.target.delSource(meta);
if (!ignoreGuards && metaGuards) {
await Promise.all(metaGuards.removed.map(removeGuard => removeGuard()));
}
}
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return h(Teleport, { to }, teleports[to]);
});
}
}
MetaManager.create = (config, resolver) => {
const resolve = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments);
}
return resolver.resolve(options, contexts, active, key, pathSegments);
};
const { addSource, delSource } = createMergedObject(resolve, active);
const mergedObject = createMergedObject(resolve, active);
// TODO: validate resolver
const manager = {
config,
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager;
app.provide(metaActiveKey, active);
},
addMeta(metaObj, vm) {
if (!vm) {
vm = getCurrentInstance() || undefined;
}
const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext);
}
// TODO: optimize initial compute
const meta = addSource(metaObj, resolveContext, true);
const unmount = () => delSource(meta);
if (vm) {
onUnmounted(unmount);
}
return {
meta,
unmount
};
},
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return h(Teleport, { to }, teleports[to]);
});
}
};
const manager = new MetaManager(config, mergedObject, resolver);
return manager;
}
};
// rollup doesnt like an import as it cant find the export so use require
const { renderToString } = require('@vue/server-renderer');
+170 -93
View File
@@ -1,5 +1,5 @@
/**
* vue-meta v3.0.0-alpha.1
* vue-meta v3.0.0-alpha.2
* (c) 2021
* - Pim (@pimlie)
* - All the amazing contributors
@@ -601,12 +601,12 @@ var VueMeta = (function (exports, vue) {
}
if (!cachedElements[attributesFor]) {
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
if ( !el) {
if (!el) {
// eslint-disable-next-line no-console
console.error('Could not find element for selector', attributesFor, ', won\'t render attributes');
return;
}
if ( el2) {
if (el2) {
// eslint-disable-next-line no-console
console.warn('Found multiple elements for selector', attributesFor);
}
@@ -630,7 +630,7 @@ var VueMeta = (function (exports, vue) {
}
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
const slot = slots && slots[slotName];
if (!slot) {
if (!slot || !isFunction(slot)) {
return content;
}
const slotScopeProps = {
@@ -653,9 +653,34 @@ var VueMeta = (function (exports, vue) {
const PolySymbol = (name) =>
// vm = vue meta
hasSymbol
? Symbol( '[vue-meta]: ' + name )
: ( '[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
? Symbol('[vue-meta]: ' + name )
: ('[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol('meta_active' );
/**
* Apply the differences between newSource & oldSource to target
*/
function applyDifference(target, newSource, oldSource) {
for (const key in newSource) {
if (!(key in oldSource)) {
target[key] = newSource[key];
continue;
}
// We dont care about nested objects here , these changes
// should already have been tracked by the MergeProxy
if (isObject(target[key])) {
continue;
}
if (newSource[key] !== oldSource[key]) {
target[key] = newSource[key];
}
}
for (const key in oldSource) {
if (!(key in newSource)) {
delete target[key];
}
}
}
function getCurrentManager(vm) {
if (!vm) {
@@ -667,15 +692,22 @@ var VueMeta = (function (exports, vue) {
return vm.appContext.config.globalProperties.$metaManager;
}
function useMeta(source, manager) {
const vm = vue.getCurrentInstance();
const vm = vue.getCurrentInstance() || undefined;
if (!manager && vm) {
manager = getCurrentManager(vm);
}
if (!manager) {
// oopsydoopsy
throw new Error('No manager or current instance');
}
return manager.addMeta(source, vm || undefined);
if (vue.isProxy(source)) {
vue.watch(source, (newSource, oldSource) => {
// We only care about first level props, second+ level will already be changed by the merge proxy
applyDifference(metaProxy.meta, newSource, oldSource);
});
source = source.value;
}
const metaProxy = manager.addMeta(source, vm);
return metaProxy;
}
function useActiveMeta() {
return vue.inject(metaActiveKey);
@@ -714,97 +746,142 @@ var VueMeta = (function (exports, vue) {
}
teleports[to].push(...nodes);
}
function createMetaManager(config, resolver) {
let cleanedUpSsr = false;
const createMetaManager = (config, resolver) => MetaManager.create(config, resolver);
class MetaManager {
constructor(config, target, resolver) {
this.ssrCleanedUp = false;
this.config = config;
this.target = target;
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
this.resolver = resolver;
}
}
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = this;
app.provide(metaActiveKey, active);
}
addMeta(metadata, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const metaGuards = ({
removed: []
});
const resolveContext = { vm };
if (this.resolver) {
this.resolver.setup(resolveContext);
}
// TODO: optimize initial compute (once)
const meta = this.target.addSource(metadata, resolveContext, true);
const onRemoved = (removeGuard) => metaGuards.removed.push(removeGuard);
const unmount = (ignoreGuards) => this.unmount(!!ignoreGuards, meta, metaGuards, vm);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
onRemoved,
unmount
};
}
unmount(ignoreGuards, meta, metaGuards, vm) {
if (vm) {
const { $el } = vm.proxy;
// Wait for element to be removed from DOM
if ($el && $el.offsetParent) {
let observer = new MutationObserver((records) => {
for (const { removedNodes } of records) {
if (!removedNodes) {
continue;
}
removedNodes.forEach((el) => {
if (el === $el && observer) {
observer.disconnect();
observer = undefined;
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
});
}
});
observer.observe($el.parentNode, { childList: true });
return;
}
}
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
async reallyUnmount(ignoreGuards, meta, metaGuards) {
this.target.delSource(meta);
if (!ignoreGuards && metaGuards) {
await Promise.all(metaGuards.removed.map(removeGuard => removeGuard()));
}
}
render({ slots } = {}) {
// TODO: clean this method
// cleanup ssr tags if not yet done
if (!this.ssrCleanedUp) {
this.ssrCleanedUp = true;
// Listen for DOM loaded because tags in the body couldnt
// have loaded yet once the manager does it first render
// (preferable there should only be one meta render on hydration)
window.addEventListener('DOMContentLoaded', () => {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
if (ssrTags && ssrTags.length) {
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el));
}
});
}
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
}
MetaManager.create = (config, resolver) => {
const resolve = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments);
}
return resolver.resolve(options, contexts, active, key, pathSegments);
};
const { addSource, delSource } = createMergedObject(resolve, active);
const mergedObject = createMergedObject(resolve, active);
// TODO: validate resolver
const manager = {
config,
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager;
app.provide(metaActiveKey, active);
},
addMeta(metaObj, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext);
}
// TODO: optimize initial compute
const meta = addSource(metaObj, resolveContext, true);
const unmount = () => delSource(meta);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
unmount
};
},
render({ slots } = {}) {
// cleanup ssr tags if not yet done
if ( !cleanedUpSsr) {
cleanedUpSsr = true;
// Listen for DOM loaded because tags in the body couldnt
// have loaded yet once the manager does it first render
// (preferable there should only be one meta render on hydration)
window.addEventListener('DOMContentLoaded', () => {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
if (ssrTags && ssrTags.length) {
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el));
}
});
}
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
};
const manager = new MetaManager(config, mergedObject, resolver);
return manager;
}
};
exports.createMetaManager = createMetaManager;
exports.deepestResolver = deepest;
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vue-meta",
"version": "3.0.0-alpha.1",
"version": "3.0.0-alpha.2",
"description": "Manage HTML metadata in Vue.js components with SSR support",
"main": "dist/vue-meta.cjs.js",
"unpkg": "dist/vue-meta.global.js",