Skip to content

Commit 3c2d9ce

Browse files
committed
chore(release): 3.0.0-alpha.2
1 parent d82508a commit 3c2d9ce

10 files changed

+859
-435
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
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.
44

5+
## [3.0.0-alpha.2](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) (2021-02-28)
6+
7+
8+
### Features
9+
10+
* add support for computed metadata ([3e1a0da](https://github.com/nuxt/vue-meta/commit/3e1a0da9e4d744f74702ae11bbe3a1bec0f0a125))
11+
512
## [3.0.0-alpha.1](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.0...v3.0.0-alpha.1) (2021-01-31)
613

714

dist/vue-meta.cjs.js

+154-77
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* vue-meta v3.0.0-alpha.1
2+
* vue-meta v3.0.0-alpha.2
33
* (c) 2021
44
* - Pim (@pimlie)
55
* - All the amazing contributors
@@ -612,7 +612,7 @@ function renderAttributes(context, key, data, config) {
612612
}
613613
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
614614
const slot = slots && slots[slotName];
615-
if (!slot) {
615+
if (!slot || !isFunction(slot)) {
616616
return content;
617617
}
618618
const slotScopeProps = {
@@ -635,9 +635,34 @@ const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag ===
635635
const PolySymbol = (name) =>
636636
// vm = vue meta
637637
hasSymbol
638-
? Symbol( '[vue-meta]: ' + name )
639-
: ( '[vue-meta]: ' ) + name;
640-
const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
638+
? Symbol('[vue-meta]: ' + name )
639+
: ('[vue-meta]: ' ) + name;
640+
const metaActiveKey = /*#__PURE__*/ PolySymbol('meta_active' );
641+
642+
/**
643+
* Apply the differences between newSource & oldSource to target
644+
*/
645+
function applyDifference(target, newSource, oldSource) {
646+
for (const key in newSource) {
647+
if (!(key in oldSource)) {
648+
target[key] = newSource[key];
649+
continue;
650+
}
651+
// We dont care about nested objects here , these changes
652+
// should already have been tracked by the MergeProxy
653+
if (isObject(target[key])) {
654+
continue;
655+
}
656+
if (newSource[key] !== oldSource[key]) {
657+
target[key] = newSource[key];
658+
}
659+
}
660+
for (const key in oldSource) {
661+
if (!(key in newSource)) {
662+
delete target[key];
663+
}
664+
}
665+
}
641666

642667
function getCurrentManager(vm) {
643668
if (!vm) {
@@ -649,15 +674,22 @@ function getCurrentManager(vm) {
649674
return vm.appContext.config.globalProperties.$metaManager;
650675
}
651676
function useMeta(source, manager) {
652-
const vm = vue.getCurrentInstance();
677+
const vm = vue.getCurrentInstance() || undefined;
653678
if (!manager && vm) {
654679
manager = getCurrentManager(vm);
655680
}
656681
if (!manager) {
657-
// oopsydoopsy
658682
throw new Error('No manager or current instance');
659683
}
660-
return manager.addMeta(source, vm || undefined);
684+
if (vue.isProxy(source)) {
685+
vue.watch(source, (newSource, oldSource) => {
686+
// We only care about first level props, second+ level will already be changed by the merge proxy
687+
applyDifference(metaProxy.meta, newSource, oldSource);
688+
});
689+
source = source.value;
690+
}
691+
const metaProxy = manager.addMeta(source, vm);
692+
return metaProxy;
661693
}
662694
function useActiveMeta() {
663695
return vue.inject(metaActiveKey);
@@ -695,83 +727,128 @@ function addVnode(teleports, to, vnodes) {
695727
}
696728
teleports[to].push(...nodes);
697729
}
698-
function createMetaManager(config, resolver) {
699-
const resolve = (options, contexts, active, key, pathSegments) => {
700-
if (isFunction(resolver)) {
701-
return resolver(options, contexts, active, key, pathSegments);
730+
const createMetaManager = (config, resolver) => MetaManager.create(config, resolver);
731+
class MetaManager {
732+
constructor(config, target, resolver) {
733+
this.ssrCleanedUp = false;
734+
this.config = config;
735+
this.target = target;
736+
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
737+
this.resolver = resolver;
702738
}
703-
return resolver.resolve(options, contexts, active, key, pathSegments);
704-
};
705-
const { addSource, delSource } = createMergedObject(resolve, active);
706-
// TODO: validate resolver
707-
const manager = {
708-
config,
709-
install(app) {
710-
app.component('Metainfo', Metainfo);
711-
app.config.globalProperties.$metaManager = manager;
712-
app.provide(metaActiveKey, active);
713-
},
714-
addMeta(metaObj, vm) {
715-
if (!vm) {
716-
vm = vue.getCurrentInstance() || undefined;
717-
}
718-
const resolveContext = { vm };
719-
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
720-
resolver.setup(resolveContext);
721-
}
722-
// TODO: optimize initial compute
723-
const meta = addSource(metaObj, resolveContext, true);
724-
const unmount = () => delSource(meta);
725-
if (vm) {
726-
vue.onUnmounted(unmount);
727-
}
728-
return {
729-
meta,
730-
unmount
731-
};
732-
},
733-
render({ slots } = {}) {
734-
const teleports = {};
735-
for (const key in active) {
736-
const config = this.config[key] || {};
737-
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
738-
if (!renderedNodes) {
739+
}
740+
install(app) {
741+
app.component('Metainfo', Metainfo);
742+
app.config.globalProperties.$metaManager = this;
743+
app.provide(metaActiveKey, active);
744+
}
745+
addMeta(metadata, vm) {
746+
if (!vm) {
747+
vm = vue.getCurrentInstance() || undefined;
748+
}
749+
const metaGuards = ({
750+
removed: []
751+
});
752+
const resolveContext = { vm };
753+
if (this.resolver) {
754+
this.resolver.setup(resolveContext);
755+
}
756+
// TODO: optimize initial compute (once)
757+
const meta = this.target.addSource(metadata, resolveContext, true);
758+
const onRemoved = (removeGuard) => metaGuards.removed.push(removeGuard);
759+
const unmount = (ignoreGuards) => this.unmount(!!ignoreGuards, meta, metaGuards, vm);
760+
if (vm) {
761+
vue.onUnmounted(unmount);
762+
}
763+
return {
764+
meta,
765+
onRemoved,
766+
unmount
767+
};
768+
}
769+
unmount(ignoreGuards, meta, metaGuards, vm) {
770+
if (vm) {
771+
const { $el } = vm.proxy;
772+
// Wait for element to be removed from DOM
773+
if ($el && $el.offsetParent) {
774+
let observer = new MutationObserver((records) => {
775+
for (const { removedNodes } of records) {
776+
if (!removedNodes) {
777+
continue;
778+
}
779+
removedNodes.forEach((el) => {
780+
if (el === $el && observer) {
781+
observer.disconnect();
782+
observer = undefined;
783+
this.reallyUnmount(ignoreGuards, meta, metaGuards);
784+
}
785+
});
786+
}
787+
});
788+
observer.observe($el.parentNode, { childList: true });
789+
return;
790+
}
791+
}
792+
this.reallyUnmount(ignoreGuards, meta, metaGuards);
793+
}
794+
async reallyUnmount(ignoreGuards, meta, metaGuards) {
795+
this.target.delSource(meta);
796+
if (!ignoreGuards && metaGuards) {
797+
await Promise.all(metaGuards.removed.map(removeGuard => removeGuard()));
798+
}
799+
}
800+
render({ slots } = {}) {
801+
const teleports = {};
802+
for (const key in active) {
803+
const config = this.config[key] || {};
804+
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
805+
if (!renderedNodes) {
806+
continue;
807+
}
808+
if (!isArray(renderedNodes)) {
809+
renderedNodes = [renderedNodes];
810+
}
811+
let defaultTo = key !== 'base' && active[key].to;
812+
if (!defaultTo && 'to' in config) {
813+
defaultTo = config.to;
814+
}
815+
if (!defaultTo && 'attributesFor' in config) {
816+
defaultTo = key;
817+
}
818+
for (const { to, vnode } of renderedNodes) {
819+
addVnode(teleports, to || defaultTo || 'head', vnode);
820+
}
821+
}
822+
if (slots) {
823+
for (const slotName in slots) {
824+
const tagName = slotName === 'default' ? 'head' : slotName;
825+
// Only teleport the contents of head/body slots
826+
if (tagName !== 'head' && tagName !== 'body') {
739827
continue;
740828
}
741-
if (!isArray(renderedNodes)) {
742-
renderedNodes = [renderedNodes];
743-
}
744-
let defaultTo = key !== 'base' && active[key].to;
745-
if (!defaultTo && 'to' in config) {
746-
defaultTo = config.to;
747-
}
748-
if (!defaultTo && 'attributesFor' in config) {
749-
defaultTo = key;
750-
}
751-
for (const { to, vnode } of renderedNodes) {
752-
addVnode(teleports, to || defaultTo || 'head', vnode);
753-
}
754-
}
755-
if (slots) {
756-
for (const slotName in slots) {
757-
const tagName = slotName === 'default' ? 'head' : slotName;
758-
// Only teleport the contents of head/body slots
759-
if (tagName !== 'head' && tagName !== 'body') {
760-
continue;
761-
}
762-
const slot = slots[slotName];
763-
if (isFunction(slot)) {
764-
addVnode(teleports, tagName, slot({ metainfo: active }));
765-
}
829+
const slot = slots[slotName];
830+
if (isFunction(slot)) {
831+
addVnode(teleports, tagName, slot({ metainfo: active }));
766832
}
767833
}
768-
return Object.keys(teleports).map((to) => {
769-
return vue.h(vue.Teleport, { to }, teleports[to]);
770-
});
771834
}
835+
return Object.keys(teleports).map((to) => {
836+
return vue.h(vue.Teleport, { to }, teleports[to]);
837+
});
838+
}
839+
}
840+
MetaManager.create = (config, resolver) => {
841+
const resolve = (options, contexts, active, key, pathSegments) => {
842+
if (isFunction(resolver)) {
843+
return resolver(options, contexts, active, key, pathSegments);
844+
}
845+
return resolver.resolve(options, contexts, active, key, pathSegments);
772846
};
847+
const mergedObject = createMergedObject(resolve, active);
848+
// TODO: validate resolver
849+
const manager = new MetaManager(config, mergedObject, resolver);
773850
return manager;
774-
}
851+
};
775852

776853
// rollup doesnt like an import as it cant find the export so use require
777854
const { renderToString } = require('@vue/server-renderer');

0 commit comments

Comments
 (0)