mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-23 12:00:33 +03:00
chore(release): 3.0.0-alpha.6
This commit is contained in:
@@ -2,6 +2,22 @@
|
|||||||
|
|
||||||
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.
|
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.6](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.5...v3.0.0-alpha.6) (2021-05-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add support for recomputing nested paths ([8c0fb63](https://github.com/nuxt/vue-meta/commit/8c0fb63f123151395d0c7afcffe2af1869a71e41))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* also delete previous values if computed value was faly ([449bb20](https://github.com/nuxt/vue-meta/commit/449bb20e6f42d0ddb837c2ba8e7d8197e887f205))
|
||||||
|
* better ssr support ([1d84787](https://github.com/nuxt/vue-meta/commit/1d847870e949ea7dac710153361185cc75ed704a))
|
||||||
|
* make types of deepest resolver compatible ([d8651be](https://github.com/nuxt/vue-meta/commit/d8651be35bd288f5e6016121b6ee384ba190afd9))
|
||||||
|
* recompute all props when assigning an object to a proxy key ([cae8e35](https://github.com/nuxt/vue-meta/commit/cae8e35340d17b72c6da4283d0043274c5dc53d3))
|
||||||
|
* rollup config, esm-bundler builds are also browser builds ([d7be9a4](https://github.com/nuxt/vue-meta/commit/d7be9a43e517910357e6232e0572a05b78e5ab5d))
|
||||||
|
|
||||||
## [3.0.0-alpha.5](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.4...v3.0.0-alpha.5) (2021-05-03)
|
## [3.0.0-alpha.5](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.4...v3.0.0-alpha.5) (2021-05-03)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
import { App } from 'vue';
|
||||||
|
import { SSRContext } from '@vue/server-renderer';
|
||||||
|
|
||||||
|
declare function renderMetaToString(app: App, ctx?: SSRContext): Promise<SSRContext>;
|
||||||
|
|
||||||
|
export { renderMetaToString };
|
||||||
Vendored
+120
-36
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* vue-meta v3.0.0-alpha.5
|
* vue-meta v3.0.0-alpha.6
|
||||||
* (c) 2021
|
* (c) 2021
|
||||||
* - Pim (@pimlie)
|
* - Pim (@pimlie)
|
||||||
* - All the amazing contributors
|
* - All the amazing contributors
|
||||||
@@ -27,7 +27,7 @@ const resolveOption = (predicament, initialValue) => (options, contexts) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function setup(context) {
|
const setup = (context) => {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
if (context.vm) {
|
if (context.vm) {
|
||||||
let { vm } = context;
|
let { vm } = context;
|
||||||
@@ -39,7 +39,7 @@ function setup(context) {
|
|||||||
} while (vm && vm.parent && vm !== vm.root);
|
} while (vm && vm.parent && vm !== vm.root);
|
||||||
}
|
}
|
||||||
context.depth = depth;
|
context.depth = depth;
|
||||||
}
|
};
|
||||||
const resolve = resolveOption((currentValue, context) => {
|
const resolve = resolveOption((currentValue, context) => {
|
||||||
const { depth } = context;
|
const { depth } = context;
|
||||||
if (!currentValue || depth > currentValue) {
|
if (!currentValue || depth > currentValue) {
|
||||||
@@ -203,7 +203,7 @@ function clone(v) {
|
|||||||
const pluck = (collection, key, callback) => {
|
const pluck = (collection, key, callback) => {
|
||||||
const plucked = [];
|
const plucked = [];
|
||||||
for (const row of collection) {
|
for (const row of collection) {
|
||||||
if (key in row) {
|
if (row && key in row) {
|
||||||
plucked.push(row[key]);
|
plucked.push(row[key]);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(row);
|
callback(row);
|
||||||
@@ -230,13 +230,23 @@ const allKeys = (source, ...sources) => {
|
|||||||
// TODO: add check for consistent types for each key (dev only)
|
// TODO: add check for consistent types for each key (dev only)
|
||||||
return keys;
|
return keys;
|
||||||
};
|
};
|
||||||
const recompute = (context, sources, target, path = []) => {
|
const recompute = (context, path = [], target, sources) => {
|
||||||
if (!path.length) {
|
const setTargetAndSources = !target && !sources;
|
||||||
if (!target) {
|
if (setTargetAndSources) {
|
||||||
target = context.active;
|
({ active: target, sources } = context);
|
||||||
}
|
if (path.length) {
|
||||||
if (!sources) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
sources = context.sources;
|
const seg = path[i];
|
||||||
|
if (!target || !target[seg]) {
|
||||||
|
{
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(`recompute: segment ${seg} not found on target`, path, target);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = target[seg];
|
||||||
|
sources = sources.map(source => source[seg]).filter(Boolean);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!target || !sources) {
|
if (!target || !sources) {
|
||||||
@@ -253,7 +263,15 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
// This assumes consistent types usages for keys across sources
|
// This assumes consistent types usages for keys across sources
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (isPlainObject(sources[0][key])) {
|
let isObject = false;
|
||||||
|
for (let i = 0; i < sources.length; i++) {
|
||||||
|
const source = sources[i];
|
||||||
|
if (source && key in source && source[key] !== undefined) {
|
||||||
|
isObject = isPlainObject(source[key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isObject) {
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
target[key] = {};
|
target[key] = {};
|
||||||
}
|
}
|
||||||
@@ -264,7 +282,7 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
keySources.push(source[key]);
|
keySources.push(source[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recompute(context, keySources, target[key], [...path, key]);
|
recompute(context, [...path, key], target[key], keySources);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ensure the target is an array if source is an array and target is empty
|
// Ensure the target is an array if source is an array and target is empty
|
||||||
@@ -278,7 +296,6 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('RESOLVED', key, resolved, 'was', target[key])
|
|
||||||
target[key] = resolved;
|
target[key] = resolved;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -309,6 +326,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (!isObject(value)) {
|
if (!isObject(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
// Also return a merge proxy for nested objects
|
||||||
if (!value[IS_PROXY]) {
|
if (!value[IS_PROXY]) {
|
||||||
const keyPath = [...pathSegments, key];
|
const keyPath = [...pathSegments, key];
|
||||||
value = createProxy(context, value, resolveContext, keyPath);
|
value = createProxy(context, value, resolveContext, keyPath);
|
||||||
@@ -346,6 +364,12 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
recompute(context);
|
recompute(context);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
else if (isPlainObject(value)) {
|
||||||
|
// if an object was assigned to this key make sure to recompute all
|
||||||
|
// of its individual properies
|
||||||
|
recompute(context, pathSegments);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -357,13 +381,13 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
||||||
// Ensure to clone if value is an object, cause sources is an array of
|
// Ensure to clone if value is an object, cause sources is an array of
|
||||||
// the sourceProxies not the sources so we could trigger an endless loop when
|
// the sourceProxies and not the sources so we could trigger an endless loop when
|
||||||
// updating a prop on an obj as the prop on the active object refers to
|
// updating a prop on an obj as the prop on the active object refers to
|
||||||
// a prop on a proxy
|
// a prop on a proxy
|
||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -376,7 +400,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
},
|
},
|
||||||
deleteProperty: (target, key) => {
|
deleteProperty: (target, key) => {
|
||||||
const success = Reflect.deleteProperty(target, key);
|
const success = Reflect.deleteProperty(target, key);
|
||||||
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
||||||
if (success) {
|
if (success) {
|
||||||
const isArrayItem = isArray(target);
|
const isArrayItem = isArray(target);
|
||||||
let activeSegmentKey;
|
let activeSegmentKey;
|
||||||
@@ -385,7 +409,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
for (const segment of pathSegments) {
|
for (const segment of pathSegments) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
proxies = proxies.map(proxy => proxy[segment]);
|
proxies = proxies.map(proxy => proxy && proxy[segment]);
|
||||||
if (isArrayItem && index === pathSegments.length - 1) {
|
if (isArrayItem && index === pathSegments.length - 1) {
|
||||||
activeSegmentKey = segment;
|
activeSegmentKey = segment;
|
||||||
break;
|
break;
|
||||||
@@ -395,7 +419,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
// Check if the key still exists in one of the sourceProxies,
|
// Check if the key still exists in one of the sourceProxies,
|
||||||
// if so resolve the new value, if not remove the key
|
// if so resolve the new value, if not remove the key
|
||||||
if (proxies.some(proxy => (key in proxy))) {
|
if (proxies.some(proxy => proxy && (key in proxy))) {
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -409,7 +433,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', resolved)
|
// console.log('SET VALUE', resolved)
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -457,6 +481,7 @@ const createMergedObject = (resolve, active) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cachedElements = {};
|
||||||
function renderMeta(context, key, data, config) {
|
function renderMeta(context, key, data, config) {
|
||||||
// console.info('renderMeta', key, data, config)
|
// console.info('renderMeta', key, data, config)
|
||||||
if ('attributesFor' in config) {
|
if ('attributesFor' in config) {
|
||||||
@@ -585,7 +610,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
// console.info('FINAL TAG', finalTag)
|
// console.info('FINAL TAG', finalTag)
|
||||||
// console.log(' ATTRIBUTES', attributes)
|
// console.log(' ATTRIBUTES', attributes)
|
||||||
// console.log(' CONTENT', content)
|
// console.log(' CONTENT', content)
|
||||||
// // console.log(data, attributes, config)
|
// console.log(data, attributes, config)
|
||||||
if (isRaw && content) {
|
if (isRaw && content) {
|
||||||
attributes.innerHTML = content;
|
attributes.innerHTML = content;
|
||||||
}
|
}
|
||||||
@@ -599,10 +624,10 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
function renderAttributes(context, key, data, config) {
|
function renderAttributes(context, key, data, config) {
|
||||||
// console.info('renderAttributes', key, data, config)
|
// console.info('renderAttributes', key, data, config)
|
||||||
const { attributesFor } = config;
|
const { attributesFor } = config;
|
||||||
if (!attributesFor) {
|
if (!attributesFor || !data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
if (context.isSSR) {
|
||||||
// render attributes in a placeholder vnode so Vue
|
// render attributes in a placeholder vnode so Vue
|
||||||
// will render the string for us
|
// will render the string for us
|
||||||
return {
|
return {
|
||||||
@@ -610,6 +635,37 @@ function renderAttributes(context, key, data, config) {
|
|||||||
vnode: vue.h(`ssr-${attributesFor}`, data)
|
vnode: vue.h(`ssr-${attributesFor}`, data)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (!cachedElements[attributesFor]) {
|
||||||
|
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
|
||||||
|
if (!el) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Could not find element for selector', attributesFor, ', won\'t render attributes');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (el2) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn('Found multiple elements for selector', attributesFor);
|
||||||
|
}
|
||||||
|
cachedElements[attributesFor] = {
|
||||||
|
el,
|
||||||
|
attrs: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { el, attrs } = cachedElements[attributesFor];
|
||||||
|
for (const attr in data) {
|
||||||
|
let content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
|
||||||
|
if (isArray(content)) {
|
||||||
|
content = content.join(',');
|
||||||
|
}
|
||||||
|
el.setAttribute(attr, content || '');
|
||||||
|
if (!attrs.includes(attr)) {
|
||||||
|
attrs.push(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const attrsToRemove = attrs.filter(attr => !data[attr]);
|
||||||
|
for (const attr of attrsToRemove) {
|
||||||
|
el.removeAttribute(attr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
|
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
|
||||||
const slot = slots && slots[slotName];
|
const slot = slots && slots[slotName];
|
||||||
@@ -658,7 +714,7 @@ function applyDifference(target, newSource, oldSource) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key in oldSource) {
|
for (const key in oldSource) {
|
||||||
if (!(key in newSource)) {
|
if (!newSource || !(key in newSource)) {
|
||||||
delete target[key];
|
delete target[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -711,9 +767,18 @@ const Metainfo = MetainfoImpl;
|
|||||||
|
|
||||||
const ssrAttribute = 'data-vm-ssr';
|
const ssrAttribute = 'data-vm-ssr';
|
||||||
const active = vue.reactive({});
|
const active = vue.reactive({});
|
||||||
function addVnode(teleports, to, vnodes) {
|
function addVnode(isSSR, teleports, to, vnodes) {
|
||||||
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
||||||
if (!to.endsWith('Attrs')) {
|
if (!isSSR) {
|
||||||
|
// Comments shouldnt have any use on the client as they are not reactive anyway
|
||||||
|
nodes.forEach((vnode, idx) => {
|
||||||
|
if (vnode.type === vue.Comment) {
|
||||||
|
nodes.splice(idx, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// only add ssrAttribute's for real meta tags
|
||||||
|
}
|
||||||
|
else if (!to.endsWith('Attrs')) {
|
||||||
nodes.forEach((vnode) => {
|
nodes.forEach((vnode) => {
|
||||||
if (!vnode.props) {
|
if (!vnode.props) {
|
||||||
vnode.props = {};
|
vnode.props = {};
|
||||||
@@ -726,10 +791,12 @@ function addVnode(teleports, to, vnodes) {
|
|||||||
}
|
}
|
||||||
teleports[to].push(...nodes);
|
teleports[to].push(...nodes);
|
||||||
}
|
}
|
||||||
const createMetaManager = (config, resolver) => MetaManager.create(config || defaultConfig, resolver || defaultResolver);
|
const createMetaManager = (isSSR = false, config, resolver) => MetaManager.create(isSSR, config || defaultConfig, resolver || defaultResolver);
|
||||||
class MetaManager {
|
class MetaManager {
|
||||||
constructor(config, target, resolver) {
|
constructor(isSSR, config, target, resolver) {
|
||||||
|
this.isSSR = false;
|
||||||
this.ssrCleanedUp = false;
|
this.ssrCleanedUp = false;
|
||||||
|
this.isSSR = isSSR;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
||||||
@@ -749,8 +816,9 @@ class MetaManager {
|
|||||||
removed: []
|
removed: []
|
||||||
});
|
});
|
||||||
const resolveContext = { vm };
|
const resolveContext = { vm };
|
||||||
if (this.resolver) {
|
const { resolver } = this;
|
||||||
this.resolver.setup(resolveContext);
|
if (resolver && resolver.setup) {
|
||||||
|
resolver.setup(resolveContext);
|
||||||
}
|
}
|
||||||
// TODO: optimize initial compute (once)
|
// TODO: optimize initial compute (once)
|
||||||
const meta = this.target.addSource(metadata, resolveContext, true);
|
const meta = this.target.addSource(metadata, resolveContext, true);
|
||||||
@@ -797,10 +865,25 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
render({ slots } = {}) {
|
render({ slots } = {}) {
|
||||||
|
// TODO: clean this method
|
||||||
|
const { isSSR } = this;
|
||||||
|
// cleanup ssr tags if not yet done
|
||||||
|
if (!isSSR && !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) {
|
||||||
|
ssrTags.forEach(el => el.parentNode && el.parentNode.removeChild(el));
|
||||||
|
}
|
||||||
|
}, { once: true });
|
||||||
|
}
|
||||||
const teleports = {};
|
const teleports = {};
|
||||||
for (const key in active) {
|
for (const key in active) {
|
||||||
const config = this.config[key] || {};
|
const config = this.config[key] || {};
|
||||||
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
|
let renderedNodes = renderMeta({ isSSR, metainfo: active, slots }, key, active[key], config);
|
||||||
if (!renderedNodes) {
|
if (!renderedNodes) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -815,7 +898,7 @@ class MetaManager {
|
|||||||
defaultTo = key;
|
defaultTo = key;
|
||||||
}
|
}
|
||||||
for (const { to, vnode } of renderedNodes) {
|
for (const { to, vnode } of renderedNodes) {
|
||||||
addVnode(teleports, to || defaultTo || 'head', vnode);
|
addVnode(this.isSSR, teleports, to || defaultTo || 'head', vnode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slots) {
|
if (slots) {
|
||||||
@@ -827,16 +910,17 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
const slot = slots[slotName];
|
const slot = slots[slotName];
|
||||||
if (isFunction(slot)) {
|
if (isFunction(slot)) {
|
||||||
addVnode(teleports, tagName, slot({ metainfo: active }));
|
addVnode(this.isSSR, teleports, tagName, slot({ metainfo: active }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.keys(teleports).map((to) => {
|
return Object.keys(teleports).map((to) => {
|
||||||
return vue.h(vue.Teleport, { to }, teleports[to]);
|
const teleport = teleports[to];
|
||||||
|
return vue.h(vue.Teleport, { to }, teleport);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MetaManager.create = (config, resolver) => {
|
MetaManager.create = (isSSR, config, resolver) => {
|
||||||
const resolve = (options, contexts, active, key, pathSegments) => {
|
const resolve = (options, contexts, active, key, pathSegments) => {
|
||||||
if (isFunction(resolver)) {
|
if (isFunction(resolver)) {
|
||||||
return resolver(options, contexts, active, key, pathSegments);
|
return resolver(options, contexts, active, key, pathSegments);
|
||||||
@@ -845,7 +929,7 @@ MetaManager.create = (config, resolver) => {
|
|||||||
};
|
};
|
||||||
const mergedObject = createMergedObject(resolve, active);
|
const mergedObject = createMergedObject(resolve, active);
|
||||||
// TODO: validate resolver
|
// TODO: validate resolver
|
||||||
const manager = new MetaManager(config, mergedObject, resolver);
|
const manager = new MetaManager(isSSR, config, mergedObject, resolver);
|
||||||
return manager;
|
return manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Vendored
+107
-36
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* vue-meta v3.0.0-alpha.5
|
* vue-meta v3.0.0-alpha.6
|
||||||
* (c) 2021
|
* (c) 2021
|
||||||
* - Pim (@pimlie)
|
* - Pim (@pimlie)
|
||||||
* - All the amazing contributors
|
* - All the amazing contributors
|
||||||
@@ -27,7 +27,7 @@ const resolveOption = (predicament, initialValue) => (options, contexts) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function setup(context) {
|
const setup = (context) => {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
if (context.vm) {
|
if (context.vm) {
|
||||||
let { vm } = context;
|
let { vm } = context;
|
||||||
@@ -39,7 +39,7 @@ function setup(context) {
|
|||||||
} while (vm && vm.parent && vm !== vm.root);
|
} while (vm && vm.parent && vm !== vm.root);
|
||||||
}
|
}
|
||||||
context.depth = depth;
|
context.depth = depth;
|
||||||
}
|
};
|
||||||
const resolve = resolveOption((currentValue, context) => {
|
const resolve = resolveOption((currentValue, context) => {
|
||||||
const { depth } = context;
|
const { depth } = context;
|
||||||
if (!currentValue || depth > currentValue) {
|
if (!currentValue || depth > currentValue) {
|
||||||
@@ -200,7 +200,7 @@ function clone(v) {
|
|||||||
const pluck = (collection, key, callback) => {
|
const pluck = (collection, key, callback) => {
|
||||||
const plucked = [];
|
const plucked = [];
|
||||||
for (const row of collection) {
|
for (const row of collection) {
|
||||||
if (key in row) {
|
if (row && key in row) {
|
||||||
plucked.push(row[key]);
|
plucked.push(row[key]);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(row);
|
callback(row);
|
||||||
@@ -227,13 +227,19 @@ const allKeys = (source, ...sources) => {
|
|||||||
// TODO: add check for consistent types for each key (dev only)
|
// TODO: add check for consistent types for each key (dev only)
|
||||||
return keys;
|
return keys;
|
||||||
};
|
};
|
||||||
const recompute = (context, sources, target, path = []) => {
|
const recompute = (context, path = [], target, sources) => {
|
||||||
if (!path.length) {
|
const setTargetAndSources = !target && !sources;
|
||||||
if (!target) {
|
if (setTargetAndSources) {
|
||||||
target = context.active;
|
({ active: target, sources } = context);
|
||||||
}
|
if (path.length) {
|
||||||
if (!sources) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
sources = context.sources;
|
const seg = path[i];
|
||||||
|
if (!target || !target[seg]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = target[seg];
|
||||||
|
sources = sources.map(source => source[seg]).filter(Boolean);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!target || !sources) {
|
if (!target || !sources) {
|
||||||
@@ -250,7 +256,15 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
// This assumes consistent types usages for keys across sources
|
// This assumes consistent types usages for keys across sources
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (isPlainObject(sources[0][key])) {
|
let isObject = false;
|
||||||
|
for (let i = 0; i < sources.length; i++) {
|
||||||
|
const source = sources[i];
|
||||||
|
if (source && key in source && source[key] !== undefined) {
|
||||||
|
isObject = isPlainObject(source[key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isObject) {
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
target[key] = {};
|
target[key] = {};
|
||||||
}
|
}
|
||||||
@@ -261,7 +275,7 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
keySources.push(source[key]);
|
keySources.push(source[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recompute(context, keySources, target[key], [...path, key]);
|
recompute(context, [...path, key], target[key], keySources);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ensure the target is an array if source is an array and target is empty
|
// Ensure the target is an array if source is an array and target is empty
|
||||||
@@ -275,7 +289,6 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('RESOLVED', key, resolved, 'was', target[key])
|
|
||||||
target[key] = resolved;
|
target[key] = resolved;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -306,6 +319,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (!isObject(value)) {
|
if (!isObject(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
// Also return a merge proxy for nested objects
|
||||||
if (!value[IS_PROXY]) {
|
if (!value[IS_PROXY]) {
|
||||||
const keyPath = [...pathSegments, key];
|
const keyPath = [...pathSegments, key];
|
||||||
value = createProxy(context, value, resolveContext, keyPath);
|
value = createProxy(context, value, resolveContext, keyPath);
|
||||||
@@ -343,6 +357,12 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
recompute(context);
|
recompute(context);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
else if (isPlainObject(value)) {
|
||||||
|
// if an object was assigned to this key make sure to recompute all
|
||||||
|
// of its individual properies
|
||||||
|
recompute(context, pathSegments);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -354,13 +374,13 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
||||||
// Ensure to clone if value is an object, cause sources is an array of
|
// Ensure to clone if value is an object, cause sources is an array of
|
||||||
// the sourceProxies not the sources so we could trigger an endless loop when
|
// the sourceProxies and not the sources so we could trigger an endless loop when
|
||||||
// updating a prop on an obj as the prop on the active object refers to
|
// updating a prop on an obj as the prop on the active object refers to
|
||||||
// a prop on a proxy
|
// a prop on a proxy
|
||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -373,7 +393,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
},
|
},
|
||||||
deleteProperty: (target, key) => {
|
deleteProperty: (target, key) => {
|
||||||
const success = Reflect.deleteProperty(target, key);
|
const success = Reflect.deleteProperty(target, key);
|
||||||
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
||||||
if (success) {
|
if (success) {
|
||||||
const isArrayItem = isArray(target);
|
const isArrayItem = isArray(target);
|
||||||
let activeSegmentKey;
|
let activeSegmentKey;
|
||||||
@@ -382,7 +402,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
for (const segment of pathSegments) {
|
for (const segment of pathSegments) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
proxies = proxies.map(proxy => proxy[segment]);
|
proxies = proxies.map(proxy => proxy && proxy[segment]);
|
||||||
if (isArrayItem && index === pathSegments.length - 1) {
|
if (isArrayItem && index === pathSegments.length - 1) {
|
||||||
activeSegmentKey = segment;
|
activeSegmentKey = segment;
|
||||||
break;
|
break;
|
||||||
@@ -392,7 +412,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
// Check if the key still exists in one of the sourceProxies,
|
// Check if the key still exists in one of the sourceProxies,
|
||||||
// if so resolve the new value, if not remove the key
|
// if so resolve the new value, if not remove the key
|
||||||
if (proxies.some(proxy => (key in proxy))) {
|
if (proxies.some(proxy => proxy && (key in proxy))) {
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -406,7 +426,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', resolved)
|
// console.log('SET VALUE', resolved)
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -454,6 +474,7 @@ const createMergedObject = (resolve, active) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cachedElements = {};
|
||||||
function renderMeta(context, key, data, config) {
|
function renderMeta(context, key, data, config) {
|
||||||
// console.info('renderMeta', key, data, config)
|
// console.info('renderMeta', key, data, config)
|
||||||
if ('attributesFor' in config) {
|
if ('attributesFor' in config) {
|
||||||
@@ -578,7 +599,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
// console.info('FINAL TAG', finalTag)
|
// console.info('FINAL TAG', finalTag)
|
||||||
// console.log(' ATTRIBUTES', attributes)
|
// console.log(' ATTRIBUTES', attributes)
|
||||||
// console.log(' CONTENT', content)
|
// console.log(' CONTENT', content)
|
||||||
// // console.log(data, attributes, config)
|
// console.log(data, attributes, config)
|
||||||
if (isRaw && content) {
|
if (isRaw && content) {
|
||||||
attributes.innerHTML = content;
|
attributes.innerHTML = content;
|
||||||
}
|
}
|
||||||
@@ -592,10 +613,10 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
function renderAttributes(context, key, data, config) {
|
function renderAttributes(context, key, data, config) {
|
||||||
// console.info('renderAttributes', key, data, config)
|
// console.info('renderAttributes', key, data, config)
|
||||||
const { attributesFor } = config;
|
const { attributesFor } = config;
|
||||||
if (!attributesFor) {
|
if (!attributesFor || !data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
if (context.isSSR) {
|
||||||
// render attributes in a placeholder vnode so Vue
|
// render attributes in a placeholder vnode so Vue
|
||||||
// will render the string for us
|
// will render the string for us
|
||||||
return {
|
return {
|
||||||
@@ -603,6 +624,28 @@ function renderAttributes(context, key, data, config) {
|
|||||||
vnode: vue.h(`ssr-${attributesFor}`, data)
|
vnode: vue.h(`ssr-${attributesFor}`, data)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (!cachedElements[attributesFor]) {
|
||||||
|
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
|
||||||
|
cachedElements[attributesFor] = {
|
||||||
|
el,
|
||||||
|
attrs: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { el, attrs } = cachedElements[attributesFor];
|
||||||
|
for (const attr in data) {
|
||||||
|
let content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
|
||||||
|
if (isArray(content)) {
|
||||||
|
content = content.join(',');
|
||||||
|
}
|
||||||
|
el.setAttribute(attr, content || '');
|
||||||
|
if (!attrs.includes(attr)) {
|
||||||
|
attrs.push(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const attrsToRemove = attrs.filter(attr => !data[attr]);
|
||||||
|
for (const attr of attrsToRemove) {
|
||||||
|
el.removeAttribute(attr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
|
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
|
||||||
const slot = slots && slots[slotName];
|
const slot = slots && slots[slotName];
|
||||||
@@ -651,7 +694,7 @@ function applyDifference(target, newSource, oldSource) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key in oldSource) {
|
for (const key in oldSource) {
|
||||||
if (!(key in newSource)) {
|
if (!newSource || !(key in newSource)) {
|
||||||
delete target[key];
|
delete target[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -704,9 +747,18 @@ const Metainfo = MetainfoImpl;
|
|||||||
|
|
||||||
const ssrAttribute = 'data-vm-ssr';
|
const ssrAttribute = 'data-vm-ssr';
|
||||||
const active = vue.reactive({});
|
const active = vue.reactive({});
|
||||||
function addVnode(teleports, to, vnodes) {
|
function addVnode(isSSR, teleports, to, vnodes) {
|
||||||
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
||||||
if (!to.endsWith('Attrs')) {
|
if (!isSSR) {
|
||||||
|
// Comments shouldnt have any use on the client as they are not reactive anyway
|
||||||
|
nodes.forEach((vnode, idx) => {
|
||||||
|
if (vnode.type === vue.Comment) {
|
||||||
|
nodes.splice(idx, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// only add ssrAttribute's for real meta tags
|
||||||
|
}
|
||||||
|
else if (!to.endsWith('Attrs')) {
|
||||||
nodes.forEach((vnode) => {
|
nodes.forEach((vnode) => {
|
||||||
if (!vnode.props) {
|
if (!vnode.props) {
|
||||||
vnode.props = {};
|
vnode.props = {};
|
||||||
@@ -719,10 +771,12 @@ function addVnode(teleports, to, vnodes) {
|
|||||||
}
|
}
|
||||||
teleports[to].push(...nodes);
|
teleports[to].push(...nodes);
|
||||||
}
|
}
|
||||||
const createMetaManager = (config, resolver) => MetaManager.create(config || defaultConfig, resolver || defaultResolver);
|
const createMetaManager = (isSSR = false, config, resolver) => MetaManager.create(isSSR, config || defaultConfig, resolver || defaultResolver);
|
||||||
class MetaManager {
|
class MetaManager {
|
||||||
constructor(config, target, resolver) {
|
constructor(isSSR, config, target, resolver) {
|
||||||
|
this.isSSR = false;
|
||||||
this.ssrCleanedUp = false;
|
this.ssrCleanedUp = false;
|
||||||
|
this.isSSR = isSSR;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
||||||
@@ -742,8 +796,9 @@ class MetaManager {
|
|||||||
removed: []
|
removed: []
|
||||||
});
|
});
|
||||||
const resolveContext = { vm };
|
const resolveContext = { vm };
|
||||||
if (this.resolver) {
|
const { resolver } = this;
|
||||||
this.resolver.setup(resolveContext);
|
if (resolver && resolver.setup) {
|
||||||
|
resolver.setup(resolveContext);
|
||||||
}
|
}
|
||||||
// TODO: optimize initial compute (once)
|
// TODO: optimize initial compute (once)
|
||||||
const meta = this.target.addSource(metadata, resolveContext, true);
|
const meta = this.target.addSource(metadata, resolveContext, true);
|
||||||
@@ -790,10 +845,25 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
render({ slots } = {}) {
|
render({ slots } = {}) {
|
||||||
|
// TODO: clean this method
|
||||||
|
const { isSSR } = this;
|
||||||
|
// cleanup ssr tags if not yet done
|
||||||
|
if (!isSSR && !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) {
|
||||||
|
ssrTags.forEach(el => el.parentNode && el.parentNode.removeChild(el));
|
||||||
|
}
|
||||||
|
}, { once: true });
|
||||||
|
}
|
||||||
const teleports = {};
|
const teleports = {};
|
||||||
for (const key in active) {
|
for (const key in active) {
|
||||||
const config = this.config[key] || {};
|
const config = this.config[key] || {};
|
||||||
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
|
let renderedNodes = renderMeta({ isSSR, metainfo: active, slots }, key, active[key], config);
|
||||||
if (!renderedNodes) {
|
if (!renderedNodes) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -808,7 +878,7 @@ class MetaManager {
|
|||||||
defaultTo = key;
|
defaultTo = key;
|
||||||
}
|
}
|
||||||
for (const { to, vnode } of renderedNodes) {
|
for (const { to, vnode } of renderedNodes) {
|
||||||
addVnode(teleports, to || defaultTo || 'head', vnode);
|
addVnode(this.isSSR, teleports, to || defaultTo || 'head', vnode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slots) {
|
if (slots) {
|
||||||
@@ -820,16 +890,17 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
const slot = slots[slotName];
|
const slot = slots[slotName];
|
||||||
if (isFunction(slot)) {
|
if (isFunction(slot)) {
|
||||||
addVnode(teleports, tagName, slot({ metainfo: active }));
|
addVnode(this.isSSR, teleports, tagName, slot({ metainfo: active }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.keys(teleports).map((to) => {
|
return Object.keys(teleports).map((to) => {
|
||||||
return vue.h(vue.Teleport, { to }, teleports[to]);
|
const teleport = teleports[to];
|
||||||
|
return vue.h(vue.Teleport, { to }, teleport);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MetaManager.create = (config, resolver) => {
|
MetaManager.create = (isSSR, config, resolver) => {
|
||||||
const resolve = (options, contexts, active, key, pathSegments) => {
|
const resolve = (options, contexts, active, key, pathSegments) => {
|
||||||
if (isFunction(resolver)) {
|
if (isFunction(resolver)) {
|
||||||
return resolver(options, contexts, active, key, pathSegments);
|
return resolver(options, contexts, active, key, pathSegments);
|
||||||
@@ -838,7 +909,7 @@ MetaManager.create = (config, resolver) => {
|
|||||||
};
|
};
|
||||||
const mergedObject = createMergedObject(resolve, active);
|
const mergedObject = createMergedObject(resolve, active);
|
||||||
// TODO: validate resolver
|
// TODO: validate resolver
|
||||||
const manager = new MetaManager(config, mergedObject, resolver);
|
const manager = new MetaManager(isSSR, config, mergedObject, resolver);
|
||||||
return manager;
|
return manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Vendored
+22
-15
@@ -1,3 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* vue-meta v3.0.0-alpha.6
|
||||||
|
* (c) 2021
|
||||||
|
* - Pim (@pimlie)
|
||||||
|
* - All the amazing contributors
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference path="ssr.d.ts" />
|
||||||
|
|
||||||
import { App, ComponentInternalInstance, Slots, VNode } from 'vue';
|
import { App, ComponentInternalInstance, Slots, VNode } from 'vue';
|
||||||
|
|
||||||
declare const IS_PROXY: unique symbol;
|
declare const IS_PROXY: unique symbol;
|
||||||
@@ -35,15 +45,16 @@ declare type MergedObjectBuilder<T> = {
|
|||||||
delSource: (sourceOrProxy: T | MergeSource<T>, recompute?: boolean) => boolean;
|
delSource: (sourceOrProxy: T | MergeSource<T>, recompute?: boolean) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type createMetaManagerMethod = (config: MetaConfig, resolver: MetaResolver | ResolveMethod) => MetaManager;
|
declare type CreateMetaManagerMethod = (isSSR: boolean, config: MetaConfig, resolver: MetaResolver | ResolveMethod) => MetaManager;
|
||||||
declare const createMetaManager: (config?: MetaConfig | undefined, resolver?: MetaResolver | undefined) => MetaManager;
|
declare const createMetaManager: (isSSR?: boolean, config?: MetaConfig | undefined, resolver?: MetaResolver | undefined) => MetaManager;
|
||||||
declare class MetaManager {
|
declare class MetaManager {
|
||||||
|
isSSR: boolean;
|
||||||
config: MetaConfig;
|
config: MetaConfig;
|
||||||
target: MergedObjectBuilder<MetaSource>;
|
target: MergedObjectBuilder<MetaSource>;
|
||||||
resolver?: MetaResolverSetup;
|
resolver?: MetaResolver;
|
||||||
ssrCleanedUp: boolean;
|
ssrCleanedUp: boolean;
|
||||||
constructor(config: MetaConfig, target: MergedObjectBuilder<MetaSource>, resolver: MetaResolver | ResolveMethod);
|
constructor(isSSR: boolean, config: MetaConfig, target: MergedObjectBuilder<MetaSource>, resolver: MetaResolver | ResolveMethod);
|
||||||
static create: createMetaManagerMethod;
|
static create: CreateMetaManagerMethod;
|
||||||
install(app: App): void;
|
install(app: App): void;
|
||||||
addMeta(metadata: MetaSource, vm?: ComponentInternalInstance): MetaProxy;
|
addMeta(metadata: MetaSource, vm?: ComponentInternalInstance): MetaProxy;
|
||||||
private unmount;
|
private unmount;
|
||||||
@@ -116,16 +127,14 @@ interface MetaActive {
|
|||||||
* Context passed to the meta resolvers
|
* Context passed to the meta resolvers
|
||||||
*/
|
*/
|
||||||
declare type MetaResolveContext = ResolveContext & {
|
declare type MetaResolveContext = ResolveContext & {
|
||||||
vm: ComponentInternalInstance | undefined;
|
vm?: ComponentInternalInstance;
|
||||||
};
|
};
|
||||||
declare type MetaResolveSetup = (context: MetaResolveContext) => void;
|
declare type MetaResolveSetup = (context: MetaResolveContext) => void;
|
||||||
declare type MetaResolver = {
|
declare type MetaResolver = {
|
||||||
setup?: MetaResolveSetup;
|
setup?: MetaResolveSetup;
|
||||||
resolve: ResolveMethod;
|
resolve: ResolveMethod;
|
||||||
};
|
};
|
||||||
declare type MetaResolverSetup = Modify<MetaResolver, {
|
declare type MetaResolverSetup = Required<MetaResolver>;
|
||||||
setup: MetaResolveSetup;
|
|
||||||
}>;
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@@ -142,8 +151,9 @@ interface MetaGuards {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
interface MetaRenderContext {
|
interface MetaRenderContext {
|
||||||
slots?: Slots;
|
isSSR: boolean;
|
||||||
metainfo: MetaActive;
|
metainfo: MetaActive;
|
||||||
|
slots?: Slots;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@@ -181,11 +191,8 @@ declare module '@vue/runtime-core' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare type MergeResolveContextDeepest = MetaResolveContext & {
|
declare const setup: MetaResolveSetup;
|
||||||
depth: number;
|
declare const resolve: ResolveMethod<any, ResolveContext>;
|
||||||
};
|
|
||||||
declare function setup(context: MergeResolveContextDeepest): void;
|
|
||||||
declare const resolve: ResolveMethod<any, MergeResolveContextDeepest>;
|
|
||||||
|
|
||||||
declare const deepest_d_setup: typeof setup;
|
declare const deepest_d_setup: typeof setup;
|
||||||
declare const deepest_d_resolve: typeof resolve;
|
declare const deepest_d_resolve: typeof resolve;
|
||||||
|
|||||||
Vendored
+87
-39
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* vue-meta v3.0.0-alpha.5
|
* vue-meta v3.0.0-alpha.6
|
||||||
* (c) 2021
|
* (c) 2021
|
||||||
* - Pim (@pimlie)
|
* - Pim (@pimlie)
|
||||||
* - All the amazing contributors
|
* - All the amazing contributors
|
||||||
@@ -23,7 +23,7 @@ const resolveOption = (predicament, initialValue) => (options, contexts) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function setup(context) {
|
const setup = (context) => {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
if (context.vm) {
|
if (context.vm) {
|
||||||
let { vm } = context;
|
let { vm } = context;
|
||||||
@@ -35,7 +35,7 @@ function setup(context) {
|
|||||||
} while (vm && vm.parent && vm !== vm.root);
|
} while (vm && vm.parent && vm !== vm.root);
|
||||||
}
|
}
|
||||||
context.depth = depth;
|
context.depth = depth;
|
||||||
}
|
};
|
||||||
const resolve = resolveOption((currentValue, context) => {
|
const resolve = resolveOption((currentValue, context) => {
|
||||||
const { depth } = context;
|
const { depth } = context;
|
||||||
if (!currentValue || depth > currentValue) {
|
if (!currentValue || depth > currentValue) {
|
||||||
@@ -199,7 +199,7 @@ function clone(v) {
|
|||||||
const pluck = (collection, key, callback) => {
|
const pluck = (collection, key, callback) => {
|
||||||
const plucked = [];
|
const plucked = [];
|
||||||
for (const row of collection) {
|
for (const row of collection) {
|
||||||
if (key in row) {
|
if (row && key in row) {
|
||||||
plucked.push(row[key]);
|
plucked.push(row[key]);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(row);
|
callback(row);
|
||||||
@@ -226,13 +226,23 @@ const allKeys = (source, ...sources) => {
|
|||||||
// TODO: add check for consistent types for each key (dev only)
|
// TODO: add check for consistent types for each key (dev only)
|
||||||
return keys;
|
return keys;
|
||||||
};
|
};
|
||||||
const recompute = (context, sources, target, path = []) => {
|
const recompute = (context, path = [], target, sources) => {
|
||||||
if (!path.length) {
|
const setTargetAndSources = !target && !sources;
|
||||||
if (!target) {
|
if (setTargetAndSources) {
|
||||||
target = context.active;
|
({ active: target, sources } = context);
|
||||||
}
|
if (path.length) {
|
||||||
if (!sources) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
sources = context.sources;
|
const seg = path[i];
|
||||||
|
if (!target || !target[seg]) {
|
||||||
|
{
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(`recompute: segment ${seg} not found on target`, path, target);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = target[seg];
|
||||||
|
sources = sources.map(source => source[seg]).filter(Boolean);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!target || !sources) {
|
if (!target || !sources) {
|
||||||
@@ -249,7 +259,15 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
// This assumes consistent types usages for keys across sources
|
// This assumes consistent types usages for keys across sources
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (isPlainObject(sources[0][key])) {
|
let isObject = false;
|
||||||
|
for (let i = 0; i < sources.length; i++) {
|
||||||
|
const source = sources[i];
|
||||||
|
if (source && key in source && source[key] !== undefined) {
|
||||||
|
isObject = isPlainObject(source[key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isObject) {
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
target[key] = {};
|
target[key] = {};
|
||||||
}
|
}
|
||||||
@@ -260,7 +278,7 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
keySources.push(source[key]);
|
keySources.push(source[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recompute(context, keySources, target[key], [...path, key]);
|
recompute(context, [...path, key], target[key], keySources);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ensure the target is an array if source is an array and target is empty
|
// Ensure the target is an array if source is an array and target is empty
|
||||||
@@ -274,7 +292,6 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('RESOLVED', key, resolved, 'was', target[key])
|
|
||||||
target[key] = resolved;
|
target[key] = resolved;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -305,6 +322,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (!isObject(value)) {
|
if (!isObject(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
// Also return a merge proxy for nested objects
|
||||||
if (!value[IS_PROXY]) {
|
if (!value[IS_PROXY]) {
|
||||||
const keyPath = [...pathSegments, key];
|
const keyPath = [...pathSegments, key];
|
||||||
value = createProxy(context, value, resolveContext, keyPath);
|
value = createProxy(context, value, resolveContext, keyPath);
|
||||||
@@ -342,6 +360,12 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
recompute(context);
|
recompute(context);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
else if (isPlainObject(value)) {
|
||||||
|
// if an object was assigned to this key make sure to recompute all
|
||||||
|
// of its individual properies
|
||||||
|
recompute(context, pathSegments);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -353,13 +377,13 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
||||||
// Ensure to clone if value is an object, cause sources is an array of
|
// Ensure to clone if value is an object, cause sources is an array of
|
||||||
// the sourceProxies not the sources so we could trigger an endless loop when
|
// the sourceProxies and not the sources so we could trigger an endless loop when
|
||||||
// updating a prop on an obj as the prop on the active object refers to
|
// updating a prop on an obj as the prop on the active object refers to
|
||||||
// a prop on a proxy
|
// a prop on a proxy
|
||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -372,7 +396,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
},
|
},
|
||||||
deleteProperty: (target, key) => {
|
deleteProperty: (target, key) => {
|
||||||
const success = Reflect.deleteProperty(target, key);
|
const success = Reflect.deleteProperty(target, key);
|
||||||
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
||||||
if (success) {
|
if (success) {
|
||||||
const isArrayItem = isArray(target);
|
const isArrayItem = isArray(target);
|
||||||
let activeSegmentKey;
|
let activeSegmentKey;
|
||||||
@@ -381,7 +405,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
for (const segment of pathSegments) {
|
for (const segment of pathSegments) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
proxies = proxies.map(proxy => proxy[segment]);
|
proxies = proxies.map(proxy => proxy && proxy[segment]);
|
||||||
if (isArrayItem && index === pathSegments.length - 1) {
|
if (isArrayItem && index === pathSegments.length - 1) {
|
||||||
activeSegmentKey = segment;
|
activeSegmentKey = segment;
|
||||||
break;
|
break;
|
||||||
@@ -391,7 +415,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
// Check if the key still exists in one of the sourceProxies,
|
// Check if the key still exists in one of the sourceProxies,
|
||||||
// if so resolve the new value, if not remove the key
|
// if so resolve the new value, if not remove the key
|
||||||
if (proxies.some(proxy => (key in proxy))) {
|
if (proxies.some(proxy => proxy && (key in proxy))) {
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -405,7 +429,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', resolved)
|
// console.log('SET VALUE', resolved)
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -582,7 +606,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
// console.info('FINAL TAG', finalTag)
|
// console.info('FINAL TAG', finalTag)
|
||||||
// console.log(' ATTRIBUTES', attributes)
|
// console.log(' ATTRIBUTES', attributes)
|
||||||
// console.log(' CONTENT', content)
|
// console.log(' CONTENT', content)
|
||||||
// // console.log(data, attributes, config)
|
// console.log(data, attributes, config)
|
||||||
if (isRaw && content) {
|
if (isRaw && content) {
|
||||||
attributes.innerHTML = content;
|
attributes.innerHTML = content;
|
||||||
}
|
}
|
||||||
@@ -596,9 +620,17 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
function renderAttributes(context, key, data, config) {
|
function renderAttributes(context, key, data, config) {
|
||||||
// console.info('renderAttributes', key, data, config)
|
// console.info('renderAttributes', key, data, config)
|
||||||
const { attributesFor } = config;
|
const { attributesFor } = config;
|
||||||
if (!attributesFor) {
|
if (!attributesFor || !data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (context.isSSR) {
|
||||||
|
// render attributes in a placeholder vnode so Vue
|
||||||
|
// will render the string for us
|
||||||
|
return {
|
||||||
|
to: '',
|
||||||
|
vnode: h(`ssr-${attributesFor}`, data)
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!cachedElements[attributesFor]) {
|
if (!cachedElements[attributesFor]) {
|
||||||
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
|
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
|
||||||
if (!el) {
|
if (!el) {
|
||||||
@@ -617,7 +649,10 @@ function renderAttributes(context, key, data, config) {
|
|||||||
}
|
}
|
||||||
const { el, attrs } = cachedElements[attributesFor];
|
const { el, attrs } = cachedElements[attributesFor];
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
const content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
|
let content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
|
||||||
|
if (isArray(content)) {
|
||||||
|
content = content.join(',');
|
||||||
|
}
|
||||||
el.setAttribute(attr, content || '');
|
el.setAttribute(attr, content || '');
|
||||||
if (!attrs.includes(attr)) {
|
if (!attrs.includes(attr)) {
|
||||||
attrs.push(attr);
|
attrs.push(attr);
|
||||||
@@ -675,7 +710,7 @@ function applyDifference(target, newSource, oldSource) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key in oldSource) {
|
for (const key in oldSource) {
|
||||||
if (!(key in newSource)) {
|
if (!newSource || !(key in newSource)) {
|
||||||
delete target[key];
|
delete target[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -728,9 +763,9 @@ const Metainfo = MetainfoImpl;
|
|||||||
|
|
||||||
const ssrAttribute = 'data-vm-ssr';
|
const ssrAttribute = 'data-vm-ssr';
|
||||||
const active = reactive({});
|
const active = reactive({});
|
||||||
function addVnode(teleports, to, vnodes) {
|
function addVnode(isSSR, teleports, to, vnodes) {
|
||||||
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
||||||
{
|
if (!isSSR) {
|
||||||
// Comments shouldnt have any use on the client as they are not reactive anyway
|
// Comments shouldnt have any use on the client as they are not reactive anyway
|
||||||
nodes.forEach((vnode, idx) => {
|
nodes.forEach((vnode, idx) => {
|
||||||
if (vnode.type === Comment) {
|
if (vnode.type === Comment) {
|
||||||
@@ -739,15 +774,25 @@ function addVnode(teleports, to, vnodes) {
|
|||||||
});
|
});
|
||||||
// only add ssrAttribute's for real meta tags
|
// only add ssrAttribute's for real meta tags
|
||||||
}
|
}
|
||||||
|
else if (!to.endsWith('Attrs')) {
|
||||||
|
nodes.forEach((vnode) => {
|
||||||
|
if (!vnode.props) {
|
||||||
|
vnode.props = {};
|
||||||
|
}
|
||||||
|
vnode.props[ssrAttribute] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!teleports[to]) {
|
if (!teleports[to]) {
|
||||||
teleports[to] = [];
|
teleports[to] = [];
|
||||||
}
|
}
|
||||||
teleports[to].push(...nodes);
|
teleports[to].push(...nodes);
|
||||||
}
|
}
|
||||||
const createMetaManager = (config, resolver) => MetaManager.create(config || defaultConfig, resolver || defaultResolver);
|
const createMetaManager = (isSSR = false, config, resolver) => MetaManager.create(isSSR, config || defaultConfig, resolver || defaultResolver);
|
||||||
class MetaManager {
|
class MetaManager {
|
||||||
constructor(config, target, resolver) {
|
constructor(isSSR, config, target, resolver) {
|
||||||
|
this.isSSR = false;
|
||||||
this.ssrCleanedUp = false;
|
this.ssrCleanedUp = false;
|
||||||
|
this.isSSR = isSSR;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
||||||
@@ -767,8 +812,9 @@ class MetaManager {
|
|||||||
removed: []
|
removed: []
|
||||||
});
|
});
|
||||||
const resolveContext = { vm };
|
const resolveContext = { vm };
|
||||||
if (this.resolver) {
|
const { resolver } = this;
|
||||||
this.resolver.setup(resolveContext);
|
if (resolver && resolver.setup) {
|
||||||
|
resolver.setup(resolveContext);
|
||||||
}
|
}
|
||||||
// TODO: optimize initial compute (once)
|
// TODO: optimize initial compute (once)
|
||||||
const meta = this.target.addSource(metadata, resolveContext, true);
|
const meta = this.target.addSource(metadata, resolveContext, true);
|
||||||
@@ -816,8 +862,9 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
render({ slots } = {}) {
|
render({ slots } = {}) {
|
||||||
// TODO: clean this method
|
// TODO: clean this method
|
||||||
|
const { isSSR } = this;
|
||||||
// cleanup ssr tags if not yet done
|
// cleanup ssr tags if not yet done
|
||||||
if (!this.ssrCleanedUp) {
|
if (!isSSR && !this.ssrCleanedUp) {
|
||||||
this.ssrCleanedUp = true;
|
this.ssrCleanedUp = true;
|
||||||
// Listen for DOM loaded because tags in the body couldnt
|
// Listen for DOM loaded because tags in the body couldnt
|
||||||
// have loaded yet once the manager does it first render
|
// have loaded yet once the manager does it first render
|
||||||
@@ -825,14 +872,14 @@ class MetaManager {
|
|||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
|
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
|
||||||
if (ssrTags && ssrTags.length) {
|
if (ssrTags && ssrTags.length) {
|
||||||
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el));
|
ssrTags.forEach(el => el.parentNode && el.parentNode.removeChild(el));
|
||||||
}
|
}
|
||||||
});
|
}, { once: true });
|
||||||
}
|
}
|
||||||
const teleports = {};
|
const teleports = {};
|
||||||
for (const key in active) {
|
for (const key in active) {
|
||||||
const config = this.config[key] || {};
|
const config = this.config[key] || {};
|
||||||
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
|
let renderedNodes = renderMeta({ isSSR, metainfo: active, slots }, key, active[key], config);
|
||||||
if (!renderedNodes) {
|
if (!renderedNodes) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -847,7 +894,7 @@ class MetaManager {
|
|||||||
defaultTo = key;
|
defaultTo = key;
|
||||||
}
|
}
|
||||||
for (const { to, vnode } of renderedNodes) {
|
for (const { to, vnode } of renderedNodes) {
|
||||||
addVnode(teleports, to || defaultTo || 'head', vnode);
|
addVnode(this.isSSR, teleports, to || defaultTo || 'head', vnode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slots) {
|
if (slots) {
|
||||||
@@ -859,16 +906,17 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
const slot = slots[slotName];
|
const slot = slots[slotName];
|
||||||
if (isFunction(slot)) {
|
if (isFunction(slot)) {
|
||||||
addVnode(teleports, tagName, slot({ metainfo: active }));
|
addVnode(this.isSSR, teleports, tagName, slot({ metainfo: active }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.keys(teleports).map((to) => {
|
return Object.keys(teleports).map((to) => {
|
||||||
return h(Teleport, { to }, teleports[to]);
|
const teleport = teleports[to];
|
||||||
|
return h(Teleport, { to }, teleport);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MetaManager.create = (config, resolver) => {
|
MetaManager.create = (isSSR, config, resolver) => {
|
||||||
const resolve = (options, contexts, active, key, pathSegments) => {
|
const resolve = (options, contexts, active, key, pathSegments) => {
|
||||||
if (isFunction(resolver)) {
|
if (isFunction(resolver)) {
|
||||||
return resolver(options, contexts, active, key, pathSegments);
|
return resolver(options, contexts, active, key, pathSegments);
|
||||||
@@ -877,7 +925,7 @@ MetaManager.create = (config, resolver) => {
|
|||||||
};
|
};
|
||||||
const mergedObject = createMergedObject(resolve, active);
|
const mergedObject = createMergedObject(resolve, active);
|
||||||
// TODO: validate resolver
|
// TODO: validate resolver
|
||||||
const manager = new MetaManager(config, mergedObject, resolver);
|
const manager = new MetaManager(isSSR, config, mergedObject, resolver);
|
||||||
return manager;
|
return manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+121
-37
@@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* vue-meta v3.0.0-alpha.5
|
* vue-meta v3.0.0-alpha.6
|
||||||
* (c) 2021
|
* (c) 2021
|
||||||
* - Pim (@pimlie)
|
* - Pim (@pimlie)
|
||||||
* - All the amazing contributors
|
* - All the amazing contributors
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { markRaw, h, getCurrentInstance, isProxy, watch, inject, defineComponent, reactive, onUnmounted, Teleport } from 'vue';
|
import { markRaw, h, getCurrentInstance, isProxy, watch, inject, defineComponent, reactive, onUnmounted, Teleport, Comment } from 'vue';
|
||||||
|
|
||||||
const resolveOption = (predicament, initialValue) => (options, contexts) => {
|
const resolveOption = (predicament, initialValue) => (options, contexts) => {
|
||||||
let resolvedIndex = -1;
|
let resolvedIndex = -1;
|
||||||
@@ -23,7 +23,7 @@ const resolveOption = (predicament, initialValue) => (options, contexts) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function setup(context) {
|
const setup = (context) => {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
if (context.vm) {
|
if (context.vm) {
|
||||||
let { vm } = context;
|
let { vm } = context;
|
||||||
@@ -35,7 +35,7 @@ function setup(context) {
|
|||||||
} while (vm && vm.parent && vm !== vm.root);
|
} while (vm && vm.parent && vm !== vm.root);
|
||||||
}
|
}
|
||||||
context.depth = depth;
|
context.depth = depth;
|
||||||
}
|
};
|
||||||
const resolve = resolveOption((currentValue, context) => {
|
const resolve = resolveOption((currentValue, context) => {
|
||||||
const { depth } = context;
|
const { depth } = context;
|
||||||
if (!currentValue || depth > currentValue) {
|
if (!currentValue || depth > currentValue) {
|
||||||
@@ -199,7 +199,7 @@ function clone(v) {
|
|||||||
const pluck = (collection, key, callback) => {
|
const pluck = (collection, key, callback) => {
|
||||||
const plucked = [];
|
const plucked = [];
|
||||||
for (const row of collection) {
|
for (const row of collection) {
|
||||||
if (key in row) {
|
if (row && key in row) {
|
||||||
plucked.push(row[key]);
|
plucked.push(row[key]);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(row);
|
callback(row);
|
||||||
@@ -226,13 +226,23 @@ const allKeys = (source, ...sources) => {
|
|||||||
// TODO: add check for consistent types for each key (dev only)
|
// TODO: add check for consistent types for each key (dev only)
|
||||||
return keys;
|
return keys;
|
||||||
};
|
};
|
||||||
const recompute = (context, sources, target, path = []) => {
|
const recompute = (context, path = [], target, sources) => {
|
||||||
if (!path.length) {
|
const setTargetAndSources = !target && !sources;
|
||||||
if (!target) {
|
if (setTargetAndSources) {
|
||||||
target = context.active;
|
({ active: target, sources } = context);
|
||||||
}
|
if (path.length) {
|
||||||
if (!sources) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
sources = context.sources;
|
const seg = path[i];
|
||||||
|
if (!target || !target[seg]) {
|
||||||
|
if (("development" !== 'production')) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(`recompute: segment ${seg} not found on target`, path, target);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = target[seg];
|
||||||
|
sources = sources.map(source => source[seg]).filter(Boolean);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!target || !sources) {
|
if (!target || !sources) {
|
||||||
@@ -249,7 +259,15 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
// This assumes consistent types usages for keys across sources
|
// This assumes consistent types usages for keys across sources
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (isPlainObject(sources[0][key])) {
|
let isObject = false;
|
||||||
|
for (let i = 0; i < sources.length; i++) {
|
||||||
|
const source = sources[i];
|
||||||
|
if (source && key in source && source[key] !== undefined) {
|
||||||
|
isObject = isPlainObject(source[key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isObject) {
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
target[key] = {};
|
target[key] = {};
|
||||||
}
|
}
|
||||||
@@ -260,7 +278,7 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
keySources.push(source[key]);
|
keySources.push(source[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recompute(context, keySources, target[key], [...path, key]);
|
recompute(context, [...path, key], target[key], keySources);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ensure the target is an array if source is an array and target is empty
|
// Ensure the target is an array if source is an array and target is empty
|
||||||
@@ -274,7 +292,6 @@ const recompute = (context, sources, target, path = []) => {
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('RESOLVED', key, resolved, 'was', target[key])
|
|
||||||
target[key] = resolved;
|
target[key] = resolved;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -305,6 +322,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (!isObject(value)) {
|
if (!isObject(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
// Also return a merge proxy for nested objects
|
||||||
if (!value[IS_PROXY]) {
|
if (!value[IS_PROXY]) {
|
||||||
const keyPath = [...pathSegments, key];
|
const keyPath = [...pathSegments, key];
|
||||||
value = createProxy(context, value, resolveContext, keyPath);
|
value = createProxy(context, value, resolveContext, keyPath);
|
||||||
@@ -342,6 +360,12 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
recompute(context);
|
recompute(context);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
else if (isPlainObject(value)) {
|
||||||
|
// if an object was assigned to this key make sure to recompute all
|
||||||
|
// of its individual properies
|
||||||
|
recompute(context, pathSegments);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -353,13 +377,13 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
||||||
// Ensure to clone if value is an object, cause sources is an array of
|
// Ensure to clone if value is an object, cause sources is an array of
|
||||||
// the sourceProxies not the sources so we could trigger an endless loop when
|
// the sourceProxies and not the sources so we could trigger an endless loop when
|
||||||
// updating a prop on an obj as the prop on the active object refers to
|
// updating a prop on an obj as the prop on the active object refers to
|
||||||
// a prop on a proxy
|
// a prop on a proxy
|
||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -372,7 +396,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
},
|
},
|
||||||
deleteProperty: (target, key) => {
|
deleteProperty: (target, key) => {
|
||||||
const success = Reflect.deleteProperty(target, key);
|
const success = Reflect.deleteProperty(target, key);
|
||||||
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
||||||
if (success) {
|
if (success) {
|
||||||
const isArrayItem = isArray(target);
|
const isArrayItem = isArray(target);
|
||||||
let activeSegmentKey;
|
let activeSegmentKey;
|
||||||
@@ -381,7 +405,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
for (const segment of pathSegments) {
|
for (const segment of pathSegments) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
proxies = proxies.map(proxy => proxy[segment]);
|
proxies = proxies.map(proxy => proxy && proxy[segment]);
|
||||||
if (isArrayItem && index === pathSegments.length - 1) {
|
if (isArrayItem && index === pathSegments.length - 1) {
|
||||||
activeSegmentKey = segment;
|
activeSegmentKey = segment;
|
||||||
break;
|
break;
|
||||||
@@ -391,7 +415,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
}
|
}
|
||||||
// Check if the key still exists in one of the sourceProxies,
|
// Check if the key still exists in one of the sourceProxies,
|
||||||
// if so resolve the new value, if not remove the key
|
// if so resolve the new value, if not remove the key
|
||||||
if (proxies.some(proxy => (key in proxy))) {
|
if (proxies.some(proxy => proxy && (key in proxy))) {
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -405,7 +429,7 @@ const createHandler = (context, resolveContext, pathSegments = []) => ({
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', resolved)
|
// console.log('SET VALUE', resolved)
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -453,6 +477,7 @@ const createMergedObject = (resolve, active) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cachedElements = {};
|
||||||
function renderMeta(context, key, data, config) {
|
function renderMeta(context, key, data, config) {
|
||||||
// console.info('renderMeta', key, data, config)
|
// console.info('renderMeta', key, data, config)
|
||||||
if ('attributesFor' in config) {
|
if ('attributesFor' in config) {
|
||||||
@@ -581,7 +606,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
// console.info('FINAL TAG', finalTag)
|
// console.info('FINAL TAG', finalTag)
|
||||||
// console.log(' ATTRIBUTES', attributes)
|
// console.log(' ATTRIBUTES', attributes)
|
||||||
// console.log(' CONTENT', content)
|
// console.log(' CONTENT', content)
|
||||||
// // console.log(data, attributes, config)
|
// console.log(data, attributes, config)
|
||||||
if (isRaw && content) {
|
if (isRaw && content) {
|
||||||
attributes.innerHTML = content;
|
attributes.innerHTML = content;
|
||||||
}
|
}
|
||||||
@@ -595,10 +620,10 @@ function renderTag(context, key, data, config = {}, groupConfig) {
|
|||||||
function renderAttributes(context, key, data, config) {
|
function renderAttributes(context, key, data, config) {
|
||||||
// console.info('renderAttributes', key, data, config)
|
// console.info('renderAttributes', key, data, config)
|
||||||
const { attributesFor } = config;
|
const { attributesFor } = config;
|
||||||
if (!attributesFor) {
|
if (!attributesFor || !data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
if (context.isSSR) {
|
||||||
// render attributes in a placeholder vnode so Vue
|
// render attributes in a placeholder vnode so Vue
|
||||||
// will render the string for us
|
// will render the string for us
|
||||||
return {
|
return {
|
||||||
@@ -606,6 +631,37 @@ function renderAttributes(context, key, data, config) {
|
|||||||
vnode: h(`ssr-${attributesFor}`, data)
|
vnode: h(`ssr-${attributesFor}`, data)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (!cachedElements[attributesFor]) {
|
||||||
|
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
|
||||||
|
if (("development" !== 'production') && !el) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Could not find element for selector', attributesFor, ', won\'t render attributes');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (("development" !== 'production') && el2) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn('Found multiple elements for selector', attributesFor);
|
||||||
|
}
|
||||||
|
cachedElements[attributesFor] = {
|
||||||
|
el,
|
||||||
|
attrs: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { el, attrs } = cachedElements[attributesFor];
|
||||||
|
for (const attr in data) {
|
||||||
|
let content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
|
||||||
|
if (isArray(content)) {
|
||||||
|
content = content.join(',');
|
||||||
|
}
|
||||||
|
el.setAttribute(attr, content || '');
|
||||||
|
if (!attrs.includes(attr)) {
|
||||||
|
attrs.push(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const attrsToRemove = attrs.filter(attr => !data[attr]);
|
||||||
|
for (const attr of attrsToRemove) {
|
||||||
|
el.removeAttribute(attr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
|
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
|
||||||
const slot = slots && slots[slotName];
|
const slot = slots && slots[slotName];
|
||||||
@@ -654,7 +710,7 @@ function applyDifference(target, newSource, oldSource) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key in oldSource) {
|
for (const key in oldSource) {
|
||||||
if (!(key in newSource)) {
|
if (!newSource || !(key in newSource)) {
|
||||||
delete target[key];
|
delete target[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,9 +763,18 @@ const Metainfo = MetainfoImpl;
|
|||||||
|
|
||||||
const ssrAttribute = 'data-vm-ssr';
|
const ssrAttribute = 'data-vm-ssr';
|
||||||
const active = reactive({});
|
const active = reactive({});
|
||||||
function addVnode(teleports, to, vnodes) {
|
function addVnode(isSSR, teleports, to, vnodes) {
|
||||||
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
||||||
if (!to.endsWith('Attrs')) {
|
if (!isSSR) {
|
||||||
|
// Comments shouldnt have any use on the client as they are not reactive anyway
|
||||||
|
nodes.forEach((vnode, idx) => {
|
||||||
|
if (vnode.type === Comment) {
|
||||||
|
nodes.splice(idx, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// only add ssrAttribute's for real meta tags
|
||||||
|
}
|
||||||
|
else if (!to.endsWith('Attrs')) {
|
||||||
nodes.forEach((vnode) => {
|
nodes.forEach((vnode) => {
|
||||||
if (!vnode.props) {
|
if (!vnode.props) {
|
||||||
vnode.props = {};
|
vnode.props = {};
|
||||||
@@ -722,10 +787,12 @@ function addVnode(teleports, to, vnodes) {
|
|||||||
}
|
}
|
||||||
teleports[to].push(...nodes);
|
teleports[to].push(...nodes);
|
||||||
}
|
}
|
||||||
const createMetaManager = (config, resolver) => MetaManager.create(config || defaultConfig, resolver || defaultResolver);
|
const createMetaManager = (isSSR = false, config, resolver) => MetaManager.create(isSSR, config || defaultConfig, resolver || defaultResolver);
|
||||||
class MetaManager {
|
class MetaManager {
|
||||||
constructor(config, target, resolver) {
|
constructor(isSSR, config, target, resolver) {
|
||||||
|
this.isSSR = false;
|
||||||
this.ssrCleanedUp = false;
|
this.ssrCleanedUp = false;
|
||||||
|
this.isSSR = isSSR;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
||||||
@@ -745,8 +812,9 @@ class MetaManager {
|
|||||||
removed: []
|
removed: []
|
||||||
});
|
});
|
||||||
const resolveContext = { vm };
|
const resolveContext = { vm };
|
||||||
if (this.resolver) {
|
const { resolver } = this;
|
||||||
this.resolver.setup(resolveContext);
|
if (resolver && resolver.setup) {
|
||||||
|
resolver.setup(resolveContext);
|
||||||
}
|
}
|
||||||
// TODO: optimize initial compute (once)
|
// TODO: optimize initial compute (once)
|
||||||
const meta = this.target.addSource(metadata, resolveContext, true);
|
const meta = this.target.addSource(metadata, resolveContext, true);
|
||||||
@@ -793,10 +861,25 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
render({ slots } = {}) {
|
render({ slots } = {}) {
|
||||||
|
// TODO: clean this method
|
||||||
|
const { isSSR } = this;
|
||||||
|
// cleanup ssr tags if not yet done
|
||||||
|
if (!isSSR && !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) {
|
||||||
|
ssrTags.forEach(el => el.parentNode && el.parentNode.removeChild(el));
|
||||||
|
}
|
||||||
|
}, { once: true });
|
||||||
|
}
|
||||||
const teleports = {};
|
const teleports = {};
|
||||||
for (const key in active) {
|
for (const key in active) {
|
||||||
const config = this.config[key] || {};
|
const config = this.config[key] || {};
|
||||||
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
|
let renderedNodes = renderMeta({ isSSR, metainfo: active, slots }, key, active[key], config);
|
||||||
if (!renderedNodes) {
|
if (!renderedNodes) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -811,7 +894,7 @@ class MetaManager {
|
|||||||
defaultTo = key;
|
defaultTo = key;
|
||||||
}
|
}
|
||||||
for (const { to, vnode } of renderedNodes) {
|
for (const { to, vnode } of renderedNodes) {
|
||||||
addVnode(teleports, to || defaultTo || 'head', vnode);
|
addVnode(this.isSSR, teleports, to || defaultTo || 'head', vnode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slots) {
|
if (slots) {
|
||||||
@@ -823,16 +906,17 @@ class MetaManager {
|
|||||||
}
|
}
|
||||||
const slot = slots[slotName];
|
const slot = slots[slotName];
|
||||||
if (isFunction(slot)) {
|
if (isFunction(slot)) {
|
||||||
addVnode(teleports, tagName, slot({ metainfo: active }));
|
addVnode(this.isSSR, teleports, tagName, slot({ metainfo: active }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.keys(teleports).map((to) => {
|
return Object.keys(teleports).map((to) => {
|
||||||
return h(Teleport, { to }, teleports[to]);
|
const teleport = teleports[to];
|
||||||
|
return h(Teleport, { to }, teleport);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MetaManager.create = (config, resolver) => {
|
MetaManager.create = (isSSR, config, resolver) => {
|
||||||
const resolve = (options, contexts, active, key, pathSegments) => {
|
const resolve = (options, contexts, active, key, pathSegments) => {
|
||||||
if (isFunction(resolver)) {
|
if (isFunction(resolver)) {
|
||||||
return resolver(options, contexts, active, key, pathSegments);
|
return resolver(options, contexts, active, key, pathSegments);
|
||||||
@@ -841,7 +925,7 @@ MetaManager.create = (config, resolver) => {
|
|||||||
};
|
};
|
||||||
const mergedObject = createMergedObject(resolve, active);
|
const mergedObject = createMergedObject(resolve, active);
|
||||||
// TODO: validate resolver
|
// TODO: validate resolver
|
||||||
const manager = new MetaManager(config, mergedObject, resolver);
|
const manager = new MetaManager(isSSR, config, mergedObject, resolver);
|
||||||
return manager;
|
return manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Vendored
+87
-39
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* vue-meta v3.0.0-alpha.5
|
* vue-meta v3.0.0-alpha.6
|
||||||
* (c) 2021
|
* (c) 2021
|
||||||
* - Pim (@pimlie)
|
* - Pim (@pimlie)
|
||||||
* - All the amazing contributors
|
* - All the amazing contributors
|
||||||
@@ -24,7 +24,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function setup(context) {
|
const setup = (context) => {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
if (context.vm) {
|
if (context.vm) {
|
||||||
let { vm } = context;
|
let { vm } = context;
|
||||||
@@ -36,7 +36,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
} while (vm && vm.parent && vm !== vm.root);
|
} while (vm && vm.parent && vm !== vm.root);
|
||||||
}
|
}
|
||||||
context.depth = depth;
|
context.depth = depth;
|
||||||
}
|
};
|
||||||
const resolve = resolveOption((currentValue, context) => {
|
const resolve = resolveOption((currentValue, context) => {
|
||||||
const { depth } = context;
|
const { depth } = context;
|
||||||
if (!currentValue || depth > currentValue) {
|
if (!currentValue || depth > currentValue) {
|
||||||
@@ -200,7 +200,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
const pluck = (collection, key, callback) => {
|
const pluck = (collection, key, callback) => {
|
||||||
const plucked = [];
|
const plucked = [];
|
||||||
for (const row of collection) {
|
for (const row of collection) {
|
||||||
if (key in row) {
|
if (row && key in row) {
|
||||||
plucked.push(row[key]);
|
plucked.push(row[key]);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(row);
|
callback(row);
|
||||||
@@ -227,13 +227,23 @@ var VueMeta = (function (exports, vue) {
|
|||||||
// TODO: add check for consistent types for each key (dev only)
|
// TODO: add check for consistent types for each key (dev only)
|
||||||
return keys;
|
return keys;
|
||||||
};
|
};
|
||||||
const recompute = (context, sources, target, path = []) => {
|
const recompute = (context, path = [], target, sources) => {
|
||||||
if (!path.length) {
|
const setTargetAndSources = !target && !sources;
|
||||||
if (!target) {
|
if (setTargetAndSources) {
|
||||||
target = context.active;
|
({ active: target, sources } = context);
|
||||||
}
|
if (path.length) {
|
||||||
if (!sources) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
sources = context.sources;
|
const seg = path[i];
|
||||||
|
if (!target || !target[seg]) {
|
||||||
|
{
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(`recompute: segment ${seg} not found on target`, path, target);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = target[seg];
|
||||||
|
sources = sources.map(source => source[seg]).filter(Boolean);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!target || !sources) {
|
if (!target || !sources) {
|
||||||
@@ -250,7 +260,15 @@ var VueMeta = (function (exports, vue) {
|
|||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
// This assumes consistent types usages for keys across sources
|
// This assumes consistent types usages for keys across sources
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (isPlainObject(sources[0][key])) {
|
let isObject = false;
|
||||||
|
for (let i = 0; i < sources.length; i++) {
|
||||||
|
const source = sources[i];
|
||||||
|
if (source && key in source && source[key] !== undefined) {
|
||||||
|
isObject = isPlainObject(source[key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isObject) {
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
target[key] = {};
|
target[key] = {};
|
||||||
}
|
}
|
||||||
@@ -261,7 +279,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
keySources.push(source[key]);
|
keySources.push(source[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recompute(context, keySources, target[key], [...path, key]);
|
recompute(context, [...path, key], target[key], keySources);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Ensure the target is an array if source is an array and target is empty
|
// Ensure the target is an array if source is an array and target is empty
|
||||||
@@ -275,7 +293,6 @@ var VueMeta = (function (exports, vue) {
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('RESOLVED', key, resolved, 'was', target[key])
|
|
||||||
target[key] = resolved;
|
target[key] = resolved;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -306,6 +323,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
if (!isObject(value)) {
|
if (!isObject(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
// Also return a merge proxy for nested objects
|
||||||
if (!value[IS_PROXY]) {
|
if (!value[IS_PROXY]) {
|
||||||
const keyPath = [...pathSegments, key];
|
const keyPath = [...pathSegments, key];
|
||||||
value = createProxy(context, value, resolveContext, keyPath);
|
value = createProxy(context, value, resolveContext, keyPath);
|
||||||
@@ -343,6 +361,12 @@ var VueMeta = (function (exports, vue) {
|
|||||||
recompute(context);
|
recompute(context);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
else if (isPlainObject(value)) {
|
||||||
|
// if an object was assigned to this key make sure to recompute all
|
||||||
|
// of its individual properies
|
||||||
|
recompute(context, pathSegments);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -354,13 +378,13 @@ var VueMeta = (function (exports, vue) {
|
|||||||
}
|
}
|
||||||
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
let resolved = context.resolve(keySources, keyContexts, active, key, pathSegments);
|
||||||
// Ensure to clone if value is an object, cause sources is an array of
|
// Ensure to clone if value is an object, cause sources is an array of
|
||||||
// the sourceProxies not the sources so we could trigger an endless loop when
|
// the sourceProxies and not the sources so we could trigger an endless loop when
|
||||||
// updating a prop on an obj as the prop on the active object refers to
|
// updating a prop on an obj as the prop on the active object refers to
|
||||||
// a prop on a proxy
|
// a prop on a proxy
|
||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
// console.log('SET VALUE', isArrayItem, key, '\nresolved:\n', resolved, '\nsources:\n', context.sources, '\nactive:\n', active, Object.keys(active))
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -373,7 +397,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
},
|
},
|
||||||
deleteProperty: (target, key) => {
|
deleteProperty: (target, key) => {
|
||||||
const success = Reflect.deleteProperty(target, key);
|
const success = Reflect.deleteProperty(target, key);
|
||||||
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
// console.warn('PROXY DELETE\nkey:', key, '\npath:', pathSegments, '\nparent:', isArray(target), target)
|
||||||
if (success) {
|
if (success) {
|
||||||
const isArrayItem = isArray(target);
|
const isArrayItem = isArray(target);
|
||||||
let activeSegmentKey;
|
let activeSegmentKey;
|
||||||
@@ -382,7 +406,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
for (const segment of pathSegments) {
|
for (const segment of pathSegments) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
proxies = proxies.map(proxy => proxy[segment]);
|
proxies = proxies.map(proxy => proxy && proxy[segment]);
|
||||||
if (isArrayItem && index === pathSegments.length - 1) {
|
if (isArrayItem && index === pathSegments.length - 1) {
|
||||||
activeSegmentKey = segment;
|
activeSegmentKey = segment;
|
||||||
break;
|
break;
|
||||||
@@ -392,7 +416,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
}
|
}
|
||||||
// Check if the key still exists in one of the sourceProxies,
|
// Check if the key still exists in one of the sourceProxies,
|
||||||
// if so resolve the new value, if not remove the key
|
// if so resolve the new value, if not remove the key
|
||||||
if (proxies.some(proxy => (key in proxy))) {
|
if (proxies.some(proxy => proxy && (key in proxy))) {
|
||||||
let keyContexts = [];
|
let keyContexts = [];
|
||||||
let keySources;
|
let keySources;
|
||||||
if (isArrayItem) {
|
if (isArrayItem) {
|
||||||
@@ -406,7 +430,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
if (isPlainObject(resolved)) {
|
if (isPlainObject(resolved)) {
|
||||||
resolved = clone(resolved);
|
resolved = clone(resolved);
|
||||||
}
|
}
|
||||||
// console.log('SET VALUE', resolved)
|
// console.log('SET VALUE', resolved)
|
||||||
if (isArrayItem && activeSegmentKey) {
|
if (isArrayItem && activeSegmentKey) {
|
||||||
active[activeSegmentKey] = resolved;
|
active[activeSegmentKey] = resolved;
|
||||||
}
|
}
|
||||||
@@ -583,7 +607,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
// console.info('FINAL TAG', finalTag)
|
// console.info('FINAL TAG', finalTag)
|
||||||
// console.log(' ATTRIBUTES', attributes)
|
// console.log(' ATTRIBUTES', attributes)
|
||||||
// console.log(' CONTENT', content)
|
// console.log(' CONTENT', content)
|
||||||
// // console.log(data, attributes, config)
|
// console.log(data, attributes, config)
|
||||||
if (isRaw && content) {
|
if (isRaw && content) {
|
||||||
attributes.innerHTML = content;
|
attributes.innerHTML = content;
|
||||||
}
|
}
|
||||||
@@ -597,9 +621,17 @@ var VueMeta = (function (exports, vue) {
|
|||||||
function renderAttributes(context, key, data, config) {
|
function renderAttributes(context, key, data, config) {
|
||||||
// console.info('renderAttributes', key, data, config)
|
// console.info('renderAttributes', key, data, config)
|
||||||
const { attributesFor } = config;
|
const { attributesFor } = config;
|
||||||
if (!attributesFor) {
|
if (!attributesFor || !data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (context.isSSR) {
|
||||||
|
// render attributes in a placeholder vnode so Vue
|
||||||
|
// will render the string for us
|
||||||
|
return {
|
||||||
|
to: '',
|
||||||
|
vnode: vue.h(`ssr-${attributesFor}`, data)
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!cachedElements[attributesFor]) {
|
if (!cachedElements[attributesFor]) {
|
||||||
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
|
const [el, el2] = Array.from(document.querySelectorAll(attributesFor));
|
||||||
if (!el) {
|
if (!el) {
|
||||||
@@ -618,7 +650,10 @@ var VueMeta = (function (exports, vue) {
|
|||||||
}
|
}
|
||||||
const { el, attrs } = cachedElements[attributesFor];
|
const { el, attrs } = cachedElements[attributesFor];
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
const content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
|
let content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
|
||||||
|
if (isArray(content)) {
|
||||||
|
content = content.join(',');
|
||||||
|
}
|
||||||
el.setAttribute(attr, content || '');
|
el.setAttribute(attr, content || '');
|
||||||
if (!attrs.includes(attr)) {
|
if (!attrs.includes(attr)) {
|
||||||
attrs.push(attr);
|
attrs.push(attr);
|
||||||
@@ -676,7 +711,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key in oldSource) {
|
for (const key in oldSource) {
|
||||||
if (!(key in newSource)) {
|
if (!newSource || !(key in newSource)) {
|
||||||
delete target[key];
|
delete target[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -729,9 +764,9 @@ var VueMeta = (function (exports, vue) {
|
|||||||
|
|
||||||
const ssrAttribute = 'data-vm-ssr';
|
const ssrAttribute = 'data-vm-ssr';
|
||||||
const active = vue.reactive({});
|
const active = vue.reactive({});
|
||||||
function addVnode(teleports, to, vnodes) {
|
function addVnode(isSSR, teleports, to, vnodes) {
|
||||||
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
|
||||||
{
|
if (!isSSR) {
|
||||||
// Comments shouldnt have any use on the client as they are not reactive anyway
|
// Comments shouldnt have any use on the client as they are not reactive anyway
|
||||||
nodes.forEach((vnode, idx) => {
|
nodes.forEach((vnode, idx) => {
|
||||||
if (vnode.type === vue.Comment) {
|
if (vnode.type === vue.Comment) {
|
||||||
@@ -740,15 +775,25 @@ var VueMeta = (function (exports, vue) {
|
|||||||
});
|
});
|
||||||
// only add ssrAttribute's for real meta tags
|
// only add ssrAttribute's for real meta tags
|
||||||
}
|
}
|
||||||
|
else if (!to.endsWith('Attrs')) {
|
||||||
|
nodes.forEach((vnode) => {
|
||||||
|
if (!vnode.props) {
|
||||||
|
vnode.props = {};
|
||||||
|
}
|
||||||
|
vnode.props[ssrAttribute] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!teleports[to]) {
|
if (!teleports[to]) {
|
||||||
teleports[to] = [];
|
teleports[to] = [];
|
||||||
}
|
}
|
||||||
teleports[to].push(...nodes);
|
teleports[to].push(...nodes);
|
||||||
}
|
}
|
||||||
const createMetaManager = (config, resolver) => MetaManager.create(config || defaultConfig, resolver || defaultResolver);
|
const createMetaManager = (isSSR = false, config, resolver) => MetaManager.create(isSSR, config || defaultConfig, resolver || defaultResolver);
|
||||||
class MetaManager {
|
class MetaManager {
|
||||||
constructor(config, target, resolver) {
|
constructor(isSSR, config, target, resolver) {
|
||||||
|
this.isSSR = false;
|
||||||
this.ssrCleanedUp = false;
|
this.ssrCleanedUp = false;
|
||||||
|
this.isSSR = isSSR;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
|
||||||
@@ -768,8 +813,9 @@ var VueMeta = (function (exports, vue) {
|
|||||||
removed: []
|
removed: []
|
||||||
});
|
});
|
||||||
const resolveContext = { vm };
|
const resolveContext = { vm };
|
||||||
if (this.resolver) {
|
const { resolver } = this;
|
||||||
this.resolver.setup(resolveContext);
|
if (resolver && resolver.setup) {
|
||||||
|
resolver.setup(resolveContext);
|
||||||
}
|
}
|
||||||
// TODO: optimize initial compute (once)
|
// TODO: optimize initial compute (once)
|
||||||
const meta = this.target.addSource(metadata, resolveContext, true);
|
const meta = this.target.addSource(metadata, resolveContext, true);
|
||||||
@@ -817,8 +863,9 @@ var VueMeta = (function (exports, vue) {
|
|||||||
}
|
}
|
||||||
render({ slots } = {}) {
|
render({ slots } = {}) {
|
||||||
// TODO: clean this method
|
// TODO: clean this method
|
||||||
|
const { isSSR } = this;
|
||||||
// cleanup ssr tags if not yet done
|
// cleanup ssr tags if not yet done
|
||||||
if (!this.ssrCleanedUp) {
|
if (!isSSR && !this.ssrCleanedUp) {
|
||||||
this.ssrCleanedUp = true;
|
this.ssrCleanedUp = true;
|
||||||
// Listen for DOM loaded because tags in the body couldnt
|
// Listen for DOM loaded because tags in the body couldnt
|
||||||
// have loaded yet once the manager does it first render
|
// have loaded yet once the manager does it first render
|
||||||
@@ -826,14 +873,14 @@ var VueMeta = (function (exports, vue) {
|
|||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
|
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
|
||||||
if (ssrTags && ssrTags.length) {
|
if (ssrTags && ssrTags.length) {
|
||||||
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el));
|
ssrTags.forEach(el => el.parentNode && el.parentNode.removeChild(el));
|
||||||
}
|
}
|
||||||
});
|
}, { once: true });
|
||||||
}
|
}
|
||||||
const teleports = {};
|
const teleports = {};
|
||||||
for (const key in active) {
|
for (const key in active) {
|
||||||
const config = this.config[key] || {};
|
const config = this.config[key] || {};
|
||||||
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
|
let renderedNodes = renderMeta({ isSSR, metainfo: active, slots }, key, active[key], config);
|
||||||
if (!renderedNodes) {
|
if (!renderedNodes) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -848,7 +895,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
defaultTo = key;
|
defaultTo = key;
|
||||||
}
|
}
|
||||||
for (const { to, vnode } of renderedNodes) {
|
for (const { to, vnode } of renderedNodes) {
|
||||||
addVnode(teleports, to || defaultTo || 'head', vnode);
|
addVnode(this.isSSR, teleports, to || defaultTo || 'head', vnode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slots) {
|
if (slots) {
|
||||||
@@ -860,16 +907,17 @@ var VueMeta = (function (exports, vue) {
|
|||||||
}
|
}
|
||||||
const slot = slots[slotName];
|
const slot = slots[slotName];
|
||||||
if (isFunction(slot)) {
|
if (isFunction(slot)) {
|
||||||
addVnode(teleports, tagName, slot({ metainfo: active }));
|
addVnode(this.isSSR, teleports, tagName, slot({ metainfo: active }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.keys(teleports).map((to) => {
|
return Object.keys(teleports).map((to) => {
|
||||||
return vue.h(vue.Teleport, { to }, teleports[to]);
|
const teleport = teleports[to];
|
||||||
|
return vue.h(vue.Teleport, { to }, teleport);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MetaManager.create = (config, resolver) => {
|
MetaManager.create = (isSSR, config, resolver) => {
|
||||||
const resolve = (options, contexts, active, key, pathSegments) => {
|
const resolve = (options, contexts, active, key, pathSegments) => {
|
||||||
if (isFunction(resolver)) {
|
if (isFunction(resolver)) {
|
||||||
return resolver(options, contexts, active, key, pathSegments);
|
return resolver(options, contexts, active, key, pathSegments);
|
||||||
@@ -878,7 +926,7 @@ var VueMeta = (function (exports, vue) {
|
|||||||
};
|
};
|
||||||
const mergedObject = createMergedObject(resolve, active);
|
const mergedObject = createMergedObject(resolve, active);
|
||||||
// TODO: validate resolver
|
// TODO: validate resolver
|
||||||
const manager = new MetaManager(config, mergedObject, resolver);
|
const manager = new MetaManager(isSSR, config, mergedObject, resolver);
|
||||||
return manager;
|
return manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-meta",
|
"name": "vue-meta",
|
||||||
"version": "3.0.0-alpha.5",
|
"version": "3.0.0-alpha.6",
|
||||||
"description": "Manage HTML metadata in Vue.js components with SSR support",
|
"description": "Manage HTML metadata in Vue.js components with SSR support",
|
||||||
"main": "dist/vue-meta.cjs.js",
|
"main": "dist/vue-meta.cjs.js",
|
||||||
"browser": "dist/vue-meta.esm-browser.min.js",
|
"browser": "dist/vue-meta.esm-browser.min.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user