Skip to content

Commit

Permalink
chore(release): 3.0.0-alpha.2
Browse files Browse the repository at this point in the history
  • Loading branch information
pimlie committed Feb 28, 2021
1 parent d82508a commit 3c2d9ce
Show file tree
Hide file tree
Showing 10 changed files with 859 additions and 435 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [3.0.0-alpha.2](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) (2021-02-28)


### Features

* add support for computed metadata ([3e1a0da](https://github.com/nuxt/vue-meta/commit/3e1a0da9e4d744f74702ae11bbe3a1bec0f0a125))

## [3.0.0-alpha.1](https://github.com/nuxt/vue-meta/compare/v3.0.0-alpha.0...v3.0.0-alpha.1) (2021-01-31)


Expand Down
231 changes: 154 additions & 77 deletions dist/vue-meta.cjs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* vue-meta v3.0.0-alpha.1
* vue-meta v3.0.0-alpha.2
* (c) 2021
* - Pim (@pimlie)
* - All the amazing contributors
Expand Down Expand Up @@ -612,7 +612,7 @@ function renderAttributes(context, key, data, config) {
}
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
const slot = slots && slots[slotName];
if (!slot) {
if (!slot || !isFunction(slot)) {
return content;
}
const slotScopeProps = {
Expand All @@ -635,9 +635,34 @@ const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag ===
const PolySymbol = (name) =>
// vm = vue meta
hasSymbol
? Symbol( '[vue-meta]: ' + name )
: ( '[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
? Symbol('[vue-meta]: ' + name )
: ('[vue-meta]: ' ) + name;
const metaActiveKey = /*#__PURE__*/ PolySymbol('meta_active' );

/**
* Apply the differences between newSource & oldSource to target
*/
function applyDifference(target, newSource, oldSource) {
for (const key in newSource) {
if (!(key in oldSource)) {
target[key] = newSource[key];
continue;
}
// We dont care about nested objects here , these changes
// should already have been tracked by the MergeProxy
if (isObject(target[key])) {
continue;
}
if (newSource[key] !== oldSource[key]) {
target[key] = newSource[key];
}
}
for (const key in oldSource) {
if (!(key in newSource)) {
delete target[key];
}
}
}

function getCurrentManager(vm) {
if (!vm) {
Expand All @@ -649,15 +674,22 @@ function getCurrentManager(vm) {
return vm.appContext.config.globalProperties.$metaManager;
}
function useMeta(source, manager) {
const vm = vue.getCurrentInstance();
const vm = vue.getCurrentInstance() || undefined;
if (!manager && vm) {
manager = getCurrentManager(vm);
}
if (!manager) {
// oopsydoopsy
throw new Error('No manager or current instance');
}
return manager.addMeta(source, vm || undefined);
if (vue.isProxy(source)) {
vue.watch(source, (newSource, oldSource) => {
// We only care about first level props, second+ level will already be changed by the merge proxy
applyDifference(metaProxy.meta, newSource, oldSource);
});
source = source.value;
}
const metaProxy = manager.addMeta(source, vm);
return metaProxy;
}
function useActiveMeta() {
return vue.inject(metaActiveKey);
Expand Down Expand Up @@ -695,83 +727,128 @@ function addVnode(teleports, to, vnodes) {
}
teleports[to].push(...nodes);
}
function createMetaManager(config, resolver) {
const resolve = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments);
const createMetaManager = (config, resolver) => MetaManager.create(config, resolver);
class MetaManager {
constructor(config, target, resolver) {
this.ssrCleanedUp = false;
this.config = config;
this.target = target;
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
this.resolver = resolver;
}
return resolver.resolve(options, contexts, active, key, pathSegments);
};
const { addSource, delSource } = createMergedObject(resolve, active);
// TODO: validate resolver
const manager = {
config,
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager;
app.provide(metaActiveKey, active);
},
addMeta(metaObj, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext);
}
// TODO: optimize initial compute
const meta = addSource(metaObj, resolveContext, true);
const unmount = () => delSource(meta);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
unmount
};
},
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
}
install(app) {
app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = this;
app.provide(metaActiveKey, active);
}
addMeta(metadata, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const metaGuards = ({
removed: []
});
const resolveContext = { vm };
if (this.resolver) {
this.resolver.setup(resolveContext);
}
// TODO: optimize initial compute (once)
const meta = this.target.addSource(metadata, resolveContext, true);
const onRemoved = (removeGuard) => metaGuards.removed.push(removeGuard);
const unmount = (ignoreGuards) => this.unmount(!!ignoreGuards, meta, metaGuards, vm);
if (vm) {
vue.onUnmounted(unmount);
}
return {
meta,
onRemoved,
unmount
};
}
unmount(ignoreGuards, meta, metaGuards, vm) {
if (vm) {
const { $el } = vm.proxy;
// Wait for element to be removed from DOM
if ($el && $el.offsetParent) {
let observer = new MutationObserver((records) => {
for (const { removedNodes } of records) {
if (!removedNodes) {
continue;
}
removedNodes.forEach((el) => {
if (el === $el && observer) {
observer.disconnect();
observer = undefined;
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
});
}
});
observer.observe($el.parentNode, { childList: true });
return;
}
}
this.reallyUnmount(ignoreGuards, meta, metaGuards);
}
async reallyUnmount(ignoreGuards, meta, metaGuards) {
this.target.delSource(meta);
if (!ignoreGuards && metaGuards) {
await Promise.all(metaGuards.removed.map(removeGuard => removeGuard()));
}
}
render({ slots } = {}) {
const teleports = {};
for (const key in active) {
const config = this.config[key] || {};
let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!renderedNodes) {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes];
}
let defaultTo = key !== 'base' && active[key].to;
if (!defaultTo && 'to' in config) {
defaultTo = config.to;
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key;
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode);
}
}
if (slots) {
for (const slotName in slots) {
const tagName = slotName === 'default' ? 'head' : slotName;
// Only teleport the contents of head/body slots
if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
}
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
return Object.keys(teleports).map((to) => {
return vue.h(vue.Teleport, { to }, teleports[to]);
});
}
}
MetaManager.create = (config, resolver) => {
const resolve = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments);
}
return resolver.resolve(options, contexts, active, key, pathSegments);
};
const mergedObject = createMergedObject(resolve, active);
// TODO: validate resolver
const manager = new MetaManager(config, mergedObject, resolver);
return manager;
}
};

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

0 comments on commit 3c2d9ce

Please sign in to comment.