2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-23 22:40:34 +03:00

fix: consolidate types

This commit is contained in:
pimlie
2021-02-01 00:08:28 +01:00
parent 616d6b4db6
commit 45704e0a31
68 changed files with 1120 additions and 1082 deletions
+5 -2
View File
@@ -4,7 +4,10 @@
], ],
"globals": { "globals": {
"__DEV__": true, "__DEV__": true,
"__BROWSER__": false, "__BROWSER__": false
},
"rules": {
"spaced-comment": ["error", "always", { "exceptions": ["#__PURE__"] }]
}, },
"overrides": [ "overrides": [
{ {
@@ -12,7 +15,7 @@
"examples/**" "examples/**"
], ],
"rules": { "rules": {
"no-console": "off", "no-console": "off"
} }
} }
] ]
+1 -1
View File
@@ -2,6 +2,6 @@
"commit-all": true, "commit-all": true,
"scripts": { "scripts": {
"postbump": "yarn build", "postbump": "yarn build",
"precommit": "git add -f dist/*" "precommit": "git add -f dist/*.js dist/*.d.ts"
} }
} }
-12
View File
@@ -1,12 +0,0 @@
import { VNodeProps } from 'vue';
import { MetainfoActive } from './types';
export interface MetainfoProps {
metainfo: MetainfoActive;
}
export declare const MetainfoImpl: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, import("vue").EmitsOptions, string, VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<{} & {}>, {}>;
export declare const Metainfo: new () => {
$props: VNodeProps & MetainfoProps;
};
//# sourceMappingURL=Metainfo.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"Metainfo.d.ts","sourceRoot":"","sources":["../../src/Metainfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,UAAU,EAAE,MAAM,KAAK,CAAA;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAExC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAA;CACzB;AAED,eAAO,MAAM,YAAY;;yPAavB,CAAA;AAEF,eAAO,MAAM,QAAQ,YACX;IACN,MAAM,EAAE,UAAU,GAAG,aAAa,CAAA;CAErC,CAAA"}
-3
View File
@@ -1,3 +0,0 @@
import { Config } from '../types';
export declare const defaultConfig: Config;
//# sourceMappingURL=default.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../src/config/default.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,eAAO,MAAM,aAAa,EAAE,MAoC3B,CAAA"}
-4
View File
@@ -1,4 +0,0 @@
import { Config } from '../types';
export declare function hasConfig(name: string, config: Config): boolean;
export declare function getConfigByKey(tagOrName: string | Array<string>, key: string, config: Config): any;
//# sourceMappingURL=helpers.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/config/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAGjC,wBAAgB,SAAS,CAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EACjC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,GAAG,CAmBL"}
-4
View File
@@ -1,4 +0,0 @@
export * from './default';
export * from './helpers';
export * from './tags';
//# sourceMappingURL=index.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA"}
-11
View File
@@ -1,11 +0,0 @@
export interface TagConfig {
keyAttribute?: string;
contentAsAttribute?: boolean | string;
attributes: boolean | Array<string>;
[key: string]: any;
}
declare const tags: {
[key: string]: TagConfig;
};
export { tags };
//# sourceMappingURL=tags.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/config/tags.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACrC,UAAU,EAAE,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,QAAA,MAAM,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAiDrC,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
-9
View File
@@ -1,9 +0,0 @@
import * as deepestResolver from './resolvers/deepest';
export { defaultConfig } from './config';
export { createMetaManager } from './manager';
export { resolveOption } from './resolvers';
export * from './ssr';
export * from './types';
export * from './useApi';
export { deepestResolver };
//# sourceMappingURL=index.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,eAAe,MAAM,qBAAqB,CAAA;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,cAAc,OAAO,CAAA;AACrB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AAExB,OAAO,EACL,eAAe,EAChB,CAAA"}
-8
View File
@@ -1,8 +0,0 @@
import { VNode } from 'vue';
import type { ResolveMethod } from './object-merge';
import type { Manager, Config, Resolver, MetainfoActive } from './types';
export declare const ssrAttribute = "data-vm-ssr";
export declare const active: MetainfoActive;
export declare function addVnode(teleports: any, to: string, _vnodes: VNode | Array<VNode>): void;
export declare function createMetaManager(config: Config, resolver: Resolver | ResolveMethod): Manager;
//# sourceMappingURL=manager.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,EAAW,MAAM,KAAK,CAAA;AAMxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAe,cAAc,EAAE,MAAM,SAAS,CAAA;AAErF,eAAO,MAAM,YAAY,gBAAgB,CAAA;AAEzC,eAAO,MAAM,MAAM,EAAE,cAA6B,CAAA;AAElD,wBAAgB,QAAQ,CAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,QA2BlF;AAED,wBAAgB,iBAAiB,CAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,aAAa,GAAG,OAAO,CAsG9F"}
-5
View File
@@ -1,5 +0,0 @@
export declare const IS_PROXY: string;
export declare const PROXY_SOURCES: string;
export declare const PROXY_TARGET: string;
export declare const RESOLVE_CONTEXT: string;
//# sourceMappingURL=constants.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/object-merge/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,QAAQ,QAA0C,CAAA;AAC/D,eAAO,MAAM,aAAa,QAA+C,CAAA;AACzE,eAAO,MAAM,YAAY,QAA8C,CAAA;AACvE,eAAO,MAAM,eAAe,QAAiD,CAAA"}
-25
View File
@@ -1,25 +0,0 @@
export declare type MergeSource = {
[key: string]: any;
};
export declare type MergedObjectValue = boolean | number | string | MergedObject | any;
export declare type MergedObject = {
[key: string]: MergedObjectValue;
};
export declare type PathSegments = Array<string>;
export declare type ResolveContext = {};
export declare type ResolveMethod = (options: Array<any>, contexts: Array<ResolveContext>, active: MergedObjectValue, key: string | number | symbol, pathSegments: PathSegments) => MergedObjectValue;
export declare type MergeContext = {
resolve: ResolveMethod;
active: MergedObject;
sources: Array<MergeSource>;
};
export declare const createMergedObject: (resolve: ResolveMethod, active?: MergedObject) => {
context: MergeContext;
active: MergedObject;
resolve: ResolveMethod;
sources: MergeSource[];
addSource: (source: MergeSource, resolveContext: ResolveContext | undefined, recompute?: Boolean) => any;
delSource: (sourceOrProxy: MergeSource, recompute?: boolean) => boolean;
compute: () => void;
};
//# sourceMappingURL=index.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/object-merge/index.ts"],"names":[],"mappings":"AAIA,oBAAY,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,CAAA;AAGD,oBAAY,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,GAAG,GAAG,CAAA;AAE9E,oBAAY,YAAY,GAAG;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAA;CACjC,CAAA;AAED,oBAAY,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;AAExC,oBAAY,cAAc,GAAG,EAAE,CAAA;AAE/B,oBAAY,aAAa,GAAG,CAC1B,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,EACnB,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,EAC/B,MAAM,EAAE,iBAAiB,EACzB,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAC7B,YAAY,EAAE,YAAY,KACvB,iBAAiB,CAAA;AAEtB,oBAAY,YAAY,GAAG;IACzB,OAAO,EAAE,aAAa,CAAA;IACtB,MAAM,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;CAC5B,CAAA;AAED,eAAO,MAAM,kBAAkB,YAAa,aAAa,WAAU,YAAY;;;;;wBAelD,WAAW,kBAAkB,cAAc,GAAG,SAAS,cAAa,OAAO;+BAUpE,WAAW,cAAa,OAAO,KAAU,OAAO;;CAyBnF,CAAA"}
-4
View File
@@ -1,4 +0,0 @@
import type { MergeContext, MergeSource, PathSegments, ResolveContext } from '.';
export declare const createProxy: (context: MergeContext, target: MergeSource, resolveContext: ResolveContext, pathSegments?: PathSegments) => any;
export declare const createHandler: (context: MergeContext, resolveContext: ResolveContext, pathSegments: PathSegments) => ProxyHandler<any>;
//# sourceMappingURL=proxy.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../../src/object-merge/proxy.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAqB,YAAY,EAAE,cAAc,EAAE,MAAM,GAAG,CAAA;AAEnG,eAAO,MAAM,WAAW,YAAa,YAAY,UAAU,WAAW,kBAAkB,cAAc,qCASrG,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,YAAY,CAAC,GAAG,CA2KjI,CAAA"}
-4
View File
@@ -1,4 +0,0 @@
import type { MergeContext, MergeSource, MergedObject, PathSegments } from '.';
export declare const allKeys: (source?: MergeSource | undefined, ...sources: Array<MergeSource>) => Array<string>;
export declare const recompute: (context: MergeContext, sources?: MergeSource[] | undefined, target?: MergedObject | undefined, path?: PathSegments) => void;
//# sourceMappingURL=recompute.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"recompute.d.ts","sourceRoot":"","sources":["../../../src/object-merge/recompute.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAkB,MAAM,GAAG,CAAA;AAE9F,eAAO,MAAM,OAAO,iDAAsC,MAAM,WAAW,CAAC,KAAG,MAAM,MAAM,CAoB1F,CAAA;AAED,eAAO,MAAM,SAAS,YAAa,YAAY,kGAAiF,IAkE/H,CAAA"}
-29
View File
@@ -1,29 +0,0 @@
import { VNode } from 'vue';
import { TODO } from './types';
export interface RenderContext {
slots: any;
[key: string]: TODO;
}
export interface GroupConfig {
group: string;
data: Array<TODO> | TODO;
tagNamespace?: string;
fullName?: string;
slotName?: string;
}
export interface SlotScopeProperties {
content: any;
metainfo: any;
[key: string]: any;
}
export declare type RenderedMetainfoNode = {
vnode: VNode;
to?: string;
};
export declare type RenderedMetainfo = Array<RenderedMetainfoNode>;
export declare function renderMeta(context: RenderContext, key: string, data: TODO, config: TODO): void | RenderedMetainfo | RenderedMetainfoNode;
export declare function renderGroup(context: RenderContext, key: string, data: TODO, config: TODO): RenderedMetainfo | RenderedMetainfoNode;
export declare function renderTag(context: RenderContext, key: string, data: TODO, config?: TODO, groupConfig?: GroupConfig): RenderedMetainfo | RenderedMetainfoNode;
export declare function renderAttributes(context: RenderContext, key: string, data: TODO, config?: TODO): RenderedMetainfoNode | void;
export declare function getSlotContent({ metainfo, slots }: RenderContext, slotName: string, content: any, groupConfig?: GroupConfig): TODO;
//# sourceMappingURL=render.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,KAAK,EAAE,MAAM,KAAK,CAAA;AAG9B,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAS9B,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAA;IACV,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,GAAG,CAAA;IACZ,QAAQ,EAAE,GAAG,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,oBAAY,oBAAoB,GAAG;IACjC,KAAK,EAAE,KAAK,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,oBAAY,gBAAgB,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAA;AAE1D,wBAAgB,UAAU,CACxB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,IAAI,GACX,IAAI,GAAG,gBAAgB,GAAG,oBAAoB,CAYhD;AAED,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,IAAI,GACX,gBAAgB,GAAG,oBAAoB,CAgCzC;AAED,wBAAgB,SAAS,CACvB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,EACV,MAAM,GAAE,IAAS,EACjB,WAAW,CAAC,EAAE,WAAW,GACxB,gBAAgB,GAAG,oBAAoB,CA8HzC;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,EACV,MAAM,GAAE,IAAS,GAChB,oBAAoB,GAAG,IAAI,CAkD7B;AAED,wBAAgB,cAAc,CAC5B,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,aAAa,EAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,GAAG,EACZ,WAAW,CAAC,EAAE,WAAW,GACxB,IAAI,CAqBN"}
-9
View File
@@ -1,9 +0,0 @@
import { ResolveMethod } from '../object-merge';
import { MetaContext } from '../types';
declare type MergeContextDeepest = MetaContext & {
depth: number;
};
export declare function setup(context: MergeContextDeepest): void;
export declare const resolve: ResolveMethod;
export {};
//# sourceMappingURL=deepest.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"deepest.d.ts","sourceRoot":"","sources":["../../../src/resolvers/deepest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAGtC,aAAK,mBAAmB,GAAG,WAAW,GAAG;IACvC,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,wBAAgB,KAAK,CAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAgBzD;AAED,eAAO,MAAM,OAAO,EAAE,aAKpB,CAAA"}
-4
View File
@@ -1,4 +0,0 @@
import { ResolveContext, ResolveMethod } from '../object-merge';
export declare type ResolveOptionReducer = (accumulator: any, context: ResolveContext) => ResolveMethod;
export declare const resolveOption: (predicament: ResolveOptionReducer) => ResolveMethod;
//# sourceMappingURL=index.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/resolvers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE/D,oBAAY,oBAAoB,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,cAAc,KAAK,aAAa,CAAA;AAE/F,eAAO,MAAM,aAAa,EAAE,CAAC,WAAW,EAAE,oBAAoB,KAAK,aAiBlE,CAAA"}
-4
View File
@@ -1,4 +0,0 @@
import type { App } from 'vue';
import type { SSRContext } from '@vue/server-renderer';
export declare function renderToStringWithMeta(app: App): Promise<[string, SSRContext]>;
//# sourceMappingURL=ssr.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../../src/ssr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAKtD,wBAAsB,sBAAsB,CAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAsBrF"}
-6
View File
@@ -1,6 +0,0 @@
import { InjectionKey } from 'vue';
import { MetainfoActive } from './types';
export declare const hasSymbol: boolean;
export declare const PolySymbol: (name: string) => string | symbol;
export declare const metaInfoKey: InjectionKey<MetainfoActive>;
//# sourceMappingURL=symbols.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../../src/symbols.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAExC,eAAO,MAAM,SAAS,SACkD,CAAA;AAExE,eAAO,MAAM,UAAU,SAAU,MAAM,oBAIM,CAAA;AAE7C,eAAO,MAAM,WAAW,8BAES,CAAA"}
-59
View File
@@ -1,59 +0,0 @@
import type { App, VNode, ComponentInternalInstance } from 'vue';
import type { MergedObject, ResolveContext, ResolveMethod } from '../object-merge';
export declare type TODO = any;
export declare type MetainfoInput = {
[key: string]: TODO;
};
export declare type MetaContext = ResolveContext & {
vm: ComponentInternalInstance | undefined;
};
export interface ConfigOption {
tag?: string;
to?: string;
group?: boolean;
keyAttribute?: string;
valueAttribute?: string;
nameless?: boolean;
namespaced?: boolean;
namespacedAttribute?: boolean;
attributesFor?: string;
}
export interface Config {
[key: string]: ConfigOption;
}
export interface MetainfoProxy extends MergedObject {
}
export interface MetainfoActive {
[key: string]: TODO;
}
export declare type MetaProxy = {
meta: MetainfoProxy;
unmount: TODO;
};
export declare type ResolveSetup = (context: MetaContext) => void;
export declare type Resolver = {
setup?: ResolveSetup;
resolve: ResolveMethod;
};
export declare type Manager = {
readonly config: Config;
install(app: App): void;
addMeta(obj: MetainfoInput, vm?: ComponentInternalInstance): MetaProxy;
render(ctx: {
slots?: any;
}): Array<VNode>;
};
declare module '@vue/runtime-core' {
interface ComponentInternalInstance {
$metaManager: Manager;
}
}
declare global {
namespace NodeJS {
interface Process {
client: boolean;
server: boolean;
}
}
}
//# sourceMappingURL=index.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,yBAAyB,EAAE,MAAM,KAAK,CAAA;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAElF,oBAAY,IAAI,GAAG,GAAG,CAAA;AAEtB,oBAAY,aAAa,GAAG;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB,CAAA;AAED,oBAAY,WAAW,GAAG,cAAc,GAAG;IACzC,EAAE,EAAE,yBAAyB,GAAG,SAAS,CAAA;CAC1C,CAAA;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,MAAM;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;CAC5B;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;CAElD;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,oBAAY,SAAS,GAAG;IACtB,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,EAAE,IAAI,CAAA;CACd,CAAA;AAED,oBAAY,YAAY,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;AAEzD,oBAAY,QAAQ,GAAG;IACrB,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,aAAa,CAAA;CACvB,CAAA;AAED,oBAAY,OAAO,GAAG;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAEvB,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,GAAG,EAAE,aAAa,EAAE,EAAE,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAA;IAEtE,MAAM,CAAC,GAAG,EAAE;QAAE,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAA;CAC3C,CAAA;AAED,OAAO,QAAQ,mBAAmB,CAAC;IACjC,UAAU,yBAAyB;QACjC,YAAY,EAAE,OAAO,CAAA;KACtB;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM,CAAC;QACf,UAAU,OAAO;YACf,MAAM,EAAE,OAAO,CAAA;YACf,MAAM,EAAE,OAAO,CAAA;SAChB;KACF;CACF"}
-6
View File
@@ -1,6 +0,0 @@
import { ComponentInternalInstance } from 'vue';
import type { Manager, MetainfoActive, MetainfoInput, MetaProxy } from './types';
export declare function getCurrentManager(vm?: ComponentInternalInstance): Manager;
export declare function useMeta(obj: MetainfoInput, manager?: Manager): MetaProxy;
export declare function useMetainfo(): MetainfoActive;
//# sourceMappingURL=useApi.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"useApi.d.ts","sourceRoot":"","sources":["../../src/useApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,yBAAyB,EAAE,MAAM,KAAK,CAAA;AAE3E,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEhF,wBAAgB,iBAAiB,CAAE,EAAE,CAAC,EAAE,yBAAyB,GAAG,OAAO,CAM1E;AAED,wBAAgB,OAAO,CAAE,GAAG,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAazE;AAED,wBAAgB,WAAW,IAAK,cAAc,CAE7C"}
-2
View File
@@ -1,2 +0,0 @@
export declare function clone(v: any): any;
//# sourceMappingURL=clone.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../../../src/utils/clone.ts"],"names":[],"mappings":"AAGA,wBAAgB,KAAK,CAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAoBlC"}
-2
View File
@@ -1,2 +0,0 @@
export declare const pluck: (collection: Array<any>, key: string, callback?: ((row: any) => void) | undefined) => any[];
//# sourceMappingURL=collection.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../../src/utils/collection.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,KAAK,eAAgB,MAAM,GAAG,CAAC,OAAO,MAAM,oBAAmB,GAAG,KAAK,IAAI,uBAcvF,CAAA"}
-13
View File
@@ -1,13 +0,0 @@
interface DebugInterface {
(...args: any[]): void;
warn: Function;
error: Function;
}
export declare const debugFn: (logFn: Function, setChildFns?: boolean) => {
(...args: any[]): void;
warn: any;
error: any;
};
export declare const debug: DebugInterface;
export {};
//# sourceMappingURL=debug.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../../src/utils/debug.ts"],"names":[],"mappings":"AACA,UAAU,cAAc;IACtB,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IACtB,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,QAAQ,CAAA;CAChB;AAED,eAAO,MAAM,OAAO,UAAW,QAAQ,gBAAe,OAAO;cACtC,GAAG,EAAE;;;CAc3B,CAAA;AAED,eAAO,MAAM,KAAK,EAAE,cAA2C,CAAA"}
-4
View File
@@ -1,4 +0,0 @@
export * from './clone';
export * from './collection';
export * from './debug';
//# sourceMappingURL=index.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA"}
+116 -113
View File
@@ -91,29 +91,8 @@ const defaultConfig = {
} }
}; };
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
/* /*
* This is a fixed config for real HTML tags * This is a fixed config for real HTML tags
*
* TODO: we probably dont need all attributes
*/ */
const tags = { const tags = {
title: { title: {
@@ -166,24 +145,34 @@ const tags = {
} }
}; };
function getConfigByKey(tagOrName, key, config) { function getTagConfigItem(tagOrName, key) {
if (config && key in config) { for (const name of tagOrName) {
return config[key]; const tag = tags[name];
} if (name && tag) {
if (isArray(tagOrName)) { return tag[key];
for (const name of tagOrName) {
if (name && name in tags) {
return tags[name][key];
}
} }
return;
}
if (tagOrName in tags) {
const tag = tags[tagOrName];
return tag[key];
} }
} }
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
// https://github.com/microsoft/TypeScript/issues/1863 // https://github.com/microsoft/TypeScript/issues/1863
const IS_PROXY = Symbol('kIsProxy'); const IS_PROXY = Symbol('kIsProxy');
const PROXY_SOURCES = Symbol('kProxySources'); const PROXY_SOURCES = Symbol('kProxySources');
@@ -443,41 +432,36 @@ const createMergedObject = (resolve, active = {}) => {
sources sources
}; };
const compute = () => recompute(context); const compute = () => recompute(context);
const addSource = (source, resolveContext, recompute = false) => { return {
const proxy = createProxy(context, source, resolveContext || {}); context,
if (recompute) { compute,
compute(); addSource: (source, resolveContext, recompute = false) => {
} const proxy = createProxy(context, source, resolveContext || {});
return proxy;
};
const delSource = (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) { if (recompute) {
compute(); compute();
} }
return true; return proxy;
},
delSource: (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) {
compute();
}
return true;
}
return false;
} }
return false;
};
return {
context,
active,
resolve,
sources,
addSource,
delSource,
compute
}; };
}; };
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 (config.attributesFor) { if ('attributesFor' in config) {
return renderAttributes(context, key, data, config); return renderAttributes(context, key, data, config);
} }
if (config.group) { if ('group' in config) {
return renderGroup(context, key, data, config); return renderGroup(context, key, data, config);
} }
return renderTag(context, key, data, config); return renderTag(context, key, data, config);
@@ -487,7 +471,7 @@ function renderGroup(context, key, data, config) {
if (isArray(data)) { if (isArray(data)) {
{ {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Specifying an array for group properties isnt supported mostly as we didnt found a use-case for this yet. If you have one, please create an issue on the vue-meta repo'); console.warn('Specifying an array for group properties isnt supported');
} }
// config.attributes = getConfigKey([key, config.tag], 'attributes', config) // config.attributes = getConfigKey([key, config.tag], 'attributes', config)
return []; return [];
@@ -513,7 +497,7 @@ function renderGroup(context, key, data, config) {
function renderTag(context, key, data, config = {}, groupConfig) { function renderTag(context, key, data, config = {}, groupConfig) {
// console.info('renderTag', key, data, config, groupConfig) // console.info('renderTag', key, data, config, groupConfig)
const contentAttributes = ['content', 'json', 'rawContent']; const contentAttributes = ['content', 'json', 'rawContent'];
const getConfig = (key) => getConfigByKey([tag, config.tag], key, config); const getTagConfig = (key) => getTagConfigItem([tag, config.tag], key);
if (isArray(data)) { if (isArray(data)) {
return data return data
.map((child) => { .map((child) => {
@@ -522,7 +506,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
.flat(); .flat();
} }
const { tag = config.tag || key } = data; const { tag = config.tag || key } = data;
let content; let content = '';
let hasChilds = false; let hasChilds = false;
let isRaw = false; let isRaw = false;
if (isString(data)) { if (isString(data)) {
@@ -574,24 +558,24 @@ function renderTag(context, key, data, config = {}, groupConfig) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
const contentAsAttribute = getConfig('contentAsAttribute'); const contentAsAttribute = !!getTagConfig('contentAsAttribute');
let valueAttribute = config.valueAttribute; let { valueAttribute } = config;
if (!valueAttribute && contentAsAttribute) { if (!valueAttribute && contentAsAttribute) {
const tagAttributes = getConfig('attributes'); const [tagAttribute] = getTagConfig('attributes');
valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute;
} }
if (!valueAttribute) { if (!valueAttribute) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
if (!config.nameless) { const { nameless, keyAttribute } = config;
const keyAttribute = getConfig('keyAttribute'); if (!nameless) {
if (keyAttribute) { if (keyAttribute) {
attributes[keyAttribute] = fullName; attributes[keyAttribute] = fullName;
} }
} }
attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig);
content = undefined; content = '';
} }
} }
const finalTag = groupConfig && groupConfig.tagNamespace const finalTag = groupConfig && groupConfig.tagNamespace
@@ -601,22 +585,22 @@ function renderTag(context, key, data, config = {}, groupConfig) {
// 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)
let vnode; if (isRaw && content) {
if (isRaw) {
attributes.innerHTML = content; attributes.innerHTML = content;
vnode = vue.h(finalTag, attributes);
}
else {
vnode = vue.h(finalTag, attributes, content);
} }
// Ignore empty string content
const vnode = vue.h(finalTag, attributes, content || undefined);
return { return {
to: data.to, to: data.to,
vnode vnode
}; };
} }
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) {
return;
}
{ {
// 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
@@ -627,19 +611,22 @@ function renderAttributes(context, key, data, config = {}) {
} }
} }
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
if (!slots || !slots[slotName]) { const slot = slots && slots[slotName];
if (!slot) {
return content; return content;
} }
const slotProps = { const slotScopeProps = {
content, content,
metainfo metainfo
}; };
if (groupConfig && groupConfig.group) { if (groupConfig && groupConfig.group) {
slotProps[groupConfig.group] = groupConfig.data; const { group, data } = groupConfig;
slotScopeProps[group] = data;
} }
const slotContent = slots[slotName](slotProps); const slotContent = slot(slotScopeProps);
if (slotContent && slotContent.length) { if (slotContent && slotContent.length) {
return slotContent[0].children; const { children } = slotContent[0];
return children ? children.toString() : '';
} }
return content; return content;
} }
@@ -650,15 +637,18 @@ const PolySymbol = (name) =>
hasSymbol hasSymbol
? Symbol( '[vue-meta]: ' + name ) ? Symbol( '[vue-meta]: ' + name )
: ( '[vue-meta]: ' ) + name; : ( '[vue-meta]: ' ) + name;
const metaInfoKey = PolySymbol( 'metainfo' ); const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
function getCurrentManager(vm) { function getCurrentManager(vm) {
if (!vm) { if (!vm) {
vm = vue.getCurrentInstance(); vm = vue.getCurrentInstance() || undefined;
}
if (!vm) {
return undefined;
} }
return vm.appContext.config.globalProperties.$metaManager; return vm.appContext.config.globalProperties.$metaManager;
} }
function useMeta(obj, manager) { function useMeta(source, manager) {
const vm = vue.getCurrentInstance(); const vm = vue.getCurrentInstance();
if (!manager && vm) { if (!manager && vm) {
manager = getCurrentManager(vm); manager = getCurrentManager(vm);
@@ -667,10 +657,10 @@ function useMeta(obj, manager) {
// oopsydoopsy // oopsydoopsy
throw new Error('No manager or current instance'); throw new Error('No manager or current instance');
} }
return manager.addMeta(obj, vm || undefined); return manager.addMeta(source, vm || undefined);
} }
function useMetainfo() { function useActiveMeta() {
return vue.inject(metaInfoKey); return vue.inject(metaActiveKey);
} }
const MetainfoImpl = vue.defineComponent({ const MetainfoImpl = vue.defineComponent({
@@ -690,23 +680,20 @@ 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(teleports, to, vnodes) {
const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
{ if (!to.endsWith('Attrs')) {
// dont add ssrAttribute for attribute vnode placeholder nodes.forEach((vnode) => {
if (!to.endsWith('Attrs')) { if (!vnode.props) {
vnodes.forEach((vnode) => { vnode.props = {};
if (!vnode.props) { }
vnode.props = {}; vnode.props[ssrAttribute] = true;
} });
vnode.props[ssrAttribute] = true;
});
}
} }
if (!teleports[to]) { if (!teleports[to]) {
teleports[to] = []; teleports[to] = [];
} }
teleports[to].push(...vnodes); teleports[to].push(...nodes);
} }
function createMetaManager(config, resolver) { function createMetaManager(config, resolver) {
const resolve = (options, contexts, active, key, pathSegments) => { const resolve = (options, contexts, active, key, pathSegments) => {
@@ -722,9 +709,12 @@ function createMetaManager(config, resolver) {
install(app) { install(app) {
app.component('Metainfo', Metainfo); app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager; app.config.globalProperties.$metaManager = manager;
app.provide(metaInfoKey, active); app.provide(metaActiveKey, active);
}, },
addMeta(metaObj, vm) { addMeta(metaObj, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const resolveContext = { vm }; const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext); resolver.setup(resolveContext);
@@ -744,21 +734,34 @@ function createMetaManager(config, resolver) {
const teleports = {}; const teleports = {};
for (const key in active) { for (const key in active) {
const config = this.config[key] || {}; const config = this.config[key] || {};
const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!vnode) { if (!renderedNodes) {
continue; continue;
} }
const vnodes = isArray(vnode) ? vnode : [vnode]; if (!isArray(renderedNodes)) {
const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); renderedNodes = [renderedNodes];
for (const { to, vnode } of vnodes) { }
addVnode(teleports, to || defaultTo, vnode); 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) { if (slots) {
for (const tag in slots) { for (const slotName in slots) {
const slotFn = slots[tag]; const tagName = slotName === 'default' ? 'head' : slotName;
if (isFunction(slotFn)) { // Only teleport the contents of head/body slots
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
} }
} }
} }
@@ -770,7 +773,7 @@ function createMetaManager(config, resolver) {
return manager; return manager;
} }
// rollup doesnt like an import, cant find export so use require // rollup doesnt like an import as it cant find the export so use require
const { renderToString } = require('@vue/server-renderer'); const { renderToString } = require('@vue/server-renderer');
async function renderToStringWithMeta(app) { async function renderToStringWithMeta(app) {
const ctx = {}; const ctx = {};
@@ -797,5 +800,5 @@ exports.defaultConfig = defaultConfig;
exports.getCurrentManager = getCurrentManager; exports.getCurrentManager = getCurrentManager;
exports.renderToStringWithMeta = renderToStringWithMeta; exports.renderToStringWithMeta = renderToStringWithMeta;
exports.resolveOption = resolveOption; exports.resolveOption = resolveOption;
exports.useActiveMeta = useActiveMeta;
exports.useMeta = useMeta; exports.useMeta = useMeta;
exports.useMetainfo = useMetainfo;
+115 -112
View File
@@ -91,29 +91,8 @@ const defaultConfig = {
} }
}; };
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
/* /*
* This is a fixed config for real HTML tags * This is a fixed config for real HTML tags
*
* TODO: we probably dont need all attributes
*/ */
const tags = { const tags = {
title: { title: {
@@ -166,24 +145,34 @@ const tags = {
} }
}; };
function getConfigByKey(tagOrName, key, config) { function getTagConfigItem(tagOrName, key) {
if (config && key in config) { for (const name of tagOrName) {
return config[key]; const tag = tags[name];
} if (name && tag) {
if (isArray(tagOrName)) { return tag[key];
for (const name of tagOrName) {
if (name && name in tags) {
return tags[name][key];
}
} }
return;
}
if (tagOrName in tags) {
const tag = tags[tagOrName];
return tag[key];
} }
} }
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
// https://github.com/microsoft/TypeScript/issues/1863 // https://github.com/microsoft/TypeScript/issues/1863
const IS_PROXY = Symbol('kIsProxy'); const IS_PROXY = Symbol('kIsProxy');
const PROXY_SOURCES = Symbol('kProxySources'); const PROXY_SOURCES = Symbol('kProxySources');
@@ -443,41 +432,36 @@ const createMergedObject = (resolve, active = {}) => {
sources sources
}; };
const compute = () => recompute(context); const compute = () => recompute(context);
const addSource = (source, resolveContext, recompute = false) => { return {
const proxy = createProxy(context, source, resolveContext || {}); context,
if (recompute) { compute,
compute(); addSource: (source, resolveContext, recompute = false) => {
} const proxy = createProxy(context, source, resolveContext || {});
return proxy;
};
const delSource = (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) { if (recompute) {
compute(); compute();
} }
return true; return proxy;
},
delSource: (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) {
compute();
}
return true;
}
return false;
} }
return false;
};
return {
context,
active,
resolve,
sources,
addSource,
delSource,
compute
}; };
}; };
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 (config.attributesFor) { if ('attributesFor' in config) {
return renderAttributes(context, key, data, config); return renderAttributes(context, key, data, config);
} }
if (config.group) { if ('group' in config) {
return renderGroup(context, key, data, config); return renderGroup(context, key, data, config);
} }
return renderTag(context, key, data, config); return renderTag(context, key, data, config);
@@ -509,7 +493,7 @@ function renderGroup(context, key, data, config) {
function renderTag(context, key, data, config = {}, groupConfig) { function renderTag(context, key, data, config = {}, groupConfig) {
// console.info('renderTag', key, data, config, groupConfig) // console.info('renderTag', key, data, config, groupConfig)
const contentAttributes = ['content', 'json', 'rawContent']; const contentAttributes = ['content', 'json', 'rawContent'];
const getConfig = (key) => getConfigByKey([tag, config.tag], key, config); const getTagConfig = (key) => getTagConfigItem([tag, config.tag], key);
if (isArray(data)) { if (isArray(data)) {
return data return data
.map((child) => { .map((child) => {
@@ -518,7 +502,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
.flat(); .flat();
} }
const { tag = config.tag || key } = data; const { tag = config.tag || key } = data;
let content; let content = '';
let hasChilds = false; let hasChilds = false;
let isRaw = false; let isRaw = false;
if (isString(data)) { if (isString(data)) {
@@ -570,24 +554,24 @@ function renderTag(context, key, data, config = {}, groupConfig) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
const contentAsAttribute = getConfig('contentAsAttribute'); const contentAsAttribute = !!getTagConfig('contentAsAttribute');
let valueAttribute = config.valueAttribute; let { valueAttribute } = config;
if (!valueAttribute && contentAsAttribute) { if (!valueAttribute && contentAsAttribute) {
const tagAttributes = getConfig('attributes'); const [tagAttribute] = getTagConfig('attributes');
valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute;
} }
if (!valueAttribute) { if (!valueAttribute) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
if (!config.nameless) { const { nameless, keyAttribute } = config;
const keyAttribute = getConfig('keyAttribute'); if (!nameless) {
if (keyAttribute) { if (keyAttribute) {
attributes[keyAttribute] = fullName; attributes[keyAttribute] = fullName;
} }
} }
attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig);
content = undefined; content = '';
} }
} }
const finalTag = groupConfig && groupConfig.tagNamespace const finalTag = groupConfig && groupConfig.tagNamespace
@@ -597,22 +581,22 @@ function renderTag(context, key, data, config = {}, groupConfig) {
// 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)
let vnode; if (isRaw && content) {
if (isRaw) {
attributes.innerHTML = content; attributes.innerHTML = content;
vnode = vue.h(finalTag, attributes);
}
else {
vnode = vue.h(finalTag, attributes, content);
} }
// Ignore empty string content
const vnode = vue.h(finalTag, attributes, content || undefined);
return { return {
to: data.to, to: data.to,
vnode vnode
}; };
} }
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) {
return;
}
{ {
// 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
@@ -623,19 +607,22 @@ function renderAttributes(context, key, data, config = {}) {
} }
} }
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
if (!slots || !slots[slotName]) { const slot = slots && slots[slotName];
if (!slot) {
return content; return content;
} }
const slotProps = { const slotScopeProps = {
content, content,
metainfo metainfo
}; };
if (groupConfig && groupConfig.group) { if (groupConfig && groupConfig.group) {
slotProps[groupConfig.group] = groupConfig.data; const { group, data } = groupConfig;
slotScopeProps[group] = data;
} }
const slotContent = slots[slotName](slotProps); const slotContent = slot(slotScopeProps);
if (slotContent && slotContent.length) { if (slotContent && slotContent.length) {
return slotContent[0].children; const { children } = slotContent[0];
return children ? children.toString() : '';
} }
return content; return content;
} }
@@ -646,15 +633,18 @@ const PolySymbol = (name) =>
hasSymbol hasSymbol
? Symbol( name) ? Symbol( name)
: ( '_vm_') + name; : ( '_vm_') + name;
const metaInfoKey = PolySymbol( 'mi'); const metaActiveKey = /*#__PURE__*/ PolySymbol( 'am');
function getCurrentManager(vm) { function getCurrentManager(vm) {
if (!vm) { if (!vm) {
vm = vue.getCurrentInstance(); vm = vue.getCurrentInstance() || undefined;
}
if (!vm) {
return undefined;
} }
return vm.appContext.config.globalProperties.$metaManager; return vm.appContext.config.globalProperties.$metaManager;
} }
function useMeta(obj, manager) { function useMeta(source, manager) {
const vm = vue.getCurrentInstance(); const vm = vue.getCurrentInstance();
if (!manager && vm) { if (!manager && vm) {
manager = getCurrentManager(vm); manager = getCurrentManager(vm);
@@ -663,10 +653,10 @@ function useMeta(obj, manager) {
// oopsydoopsy // oopsydoopsy
throw new Error('No manager or current instance'); throw new Error('No manager or current instance');
} }
return manager.addMeta(obj, vm || undefined); return manager.addMeta(source, vm || undefined);
} }
function useMetainfo() { function useActiveMeta() {
return vue.inject(metaInfoKey); return vue.inject(metaActiveKey);
} }
const MetainfoImpl = vue.defineComponent({ const MetainfoImpl = vue.defineComponent({
@@ -686,23 +676,20 @@ 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(teleports, to, vnodes) {
const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
{ if (!to.endsWith('Attrs')) {
// dont add ssrAttribute for attribute vnode placeholder nodes.forEach((vnode) => {
if (!to.endsWith('Attrs')) { if (!vnode.props) {
vnodes.forEach((vnode) => { vnode.props = {};
if (!vnode.props) { }
vnode.props = {}; vnode.props[ssrAttribute] = true;
} });
vnode.props[ssrAttribute] = true;
});
}
} }
if (!teleports[to]) { if (!teleports[to]) {
teleports[to] = []; teleports[to] = [];
} }
teleports[to].push(...vnodes); teleports[to].push(...nodes);
} }
function createMetaManager(config, resolver) { function createMetaManager(config, resolver) {
const resolve = (options, contexts, active, key, pathSegments) => { const resolve = (options, contexts, active, key, pathSegments) => {
@@ -718,9 +705,12 @@ function createMetaManager(config, resolver) {
install(app) { install(app) {
app.component('Metainfo', Metainfo); app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager; app.config.globalProperties.$metaManager = manager;
app.provide(metaInfoKey, active); app.provide(metaActiveKey, active);
}, },
addMeta(metaObj, vm) { addMeta(metaObj, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const resolveContext = { vm }; const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext); resolver.setup(resolveContext);
@@ -740,21 +730,34 @@ function createMetaManager(config, resolver) {
const teleports = {}; const teleports = {};
for (const key in active) { for (const key in active) {
const config = this.config[key] || {}; const config = this.config[key] || {};
const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!vnode) { if (!renderedNodes) {
continue; continue;
} }
const vnodes = isArray(vnode) ? vnode : [vnode]; if (!isArray(renderedNodes)) {
const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); renderedNodes = [renderedNodes];
for (const { to, vnode } of vnodes) { }
addVnode(teleports, to || defaultTo, vnode); 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) { if (slots) {
for (const tag in slots) { for (const slotName in slots) {
const slotFn = slots[tag]; const tagName = slotName === 'default' ? 'head' : slotName;
if (isFunction(slotFn)) { // Only teleport the contents of head/body slots
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
} }
} }
} }
@@ -766,7 +769,7 @@ function createMetaManager(config, resolver) {
return manager; return manager;
} }
// rollup doesnt like an import, cant find export so use require // rollup doesnt like an import as it cant find the export so use require
const { renderToString } = require('@vue/server-renderer'); const { renderToString } = require('@vue/server-renderer');
async function renderToStringWithMeta(app) { async function renderToStringWithMeta(app) {
const ctx = {}; const ctx = {};
@@ -793,5 +796,5 @@ exports.defaultConfig = defaultConfig;
exports.getCurrentManager = getCurrentManager; exports.getCurrentManager = getCurrentManager;
exports.renderToStringWithMeta = renderToStringWithMeta; exports.renderToStringWithMeta = renderToStringWithMeta;
exports.resolveOption = resolveOption; exports.resolveOption = resolveOption;
exports.useActiveMeta = useActiveMeta;
exports.useMeta = useMeta; exports.useMeta = useMeta;
exports.useMetainfo = useMetainfo;
+166
View File
@@ -0,0 +1,166 @@
import { ComponentInternalInstance, App, Slots, VNode } from 'vue';
import { SSRContext } from '@vue/server-renderer';
declare type MergedObjectValue = boolean | number | string | MergedObject | any;
declare type MergedObject = {
[key: string]: MergedObjectValue;
};
declare type PathSegments = Array<string>;
declare type ResolveContext = {};
declare type ResolveMethod = (options: Array<any>, contexts: Array<ResolveContext>, active: MergedObjectValue, key: string | number | symbol, pathSegments: PathSegments) => MergedObjectValue;
declare type MetaConfigSectionKey = 'tag' | 'to' | 'keyAttribute' | 'valueAttribute' | 'nameless' | 'group' | 'namespaced' | 'namespacedAttribute' | 'attributesFor';
interface MetaConfigSectionTag {
tag?: string;
to?: string;
keyAttribute?: string;
valueAttribute?: string;
nameless?: boolean;
}
declare type MetaConfigSectionGroup = {
group: boolean;
namespaced?: boolean;
namespacedAttribute?: boolean;
};
declare type MetaConfigSectionAttribute = {
attributesFor: string;
};
declare type MetaConfigSection = MetaConfigSectionGroup | MetaConfigSectionTag | MetaConfigSectionAttribute;
interface MetaConfig {
[key: string]: MetaConfigSection;
}
declare type MetaTagConfigKey = 'keyAttribute' | 'contentAsAttribute' | 'attributes';
interface MetaTagConfig {
keyAttribute?: string;
contentAsAttribute?: boolean | string;
attributes: boolean | Array<string>;
}
declare type MetaTagName = 'title' | 'base' | 'meta' | 'link' | 'style' | 'script' | 'noscript';
declare type MetaTagsConfig = {
[key in MetaTagName]: MetaTagConfig;
};
declare type TODO = any;
/**
* Proxied meta source for tracking changes and updating the active meta daa
*/
interface MetaSourceProxy extends MergedObject {
}
/**
* Metainfo data source input by the user through the useMeta fn
*/
declare type MetaSource = {
[key: string]: TODO;
};
/**
* Return value of the useMeta api
*/
declare type MetaProxy = {
meta: MetaSourceProxy;
unmount: Function | false;
};
/**
* The active/aggregated meta data currently rendered
*/
interface MetaActive {
[key: string]: TODO;
}
/**
* Context passed to the meta resolvers
*/
declare type MetaResolveContext = ResolveContext & {
vm: ComponentInternalInstance | undefined;
};
declare type MetaResolveSetup = (context: MetaResolveContext) => void;
declare type MetaResolver = {
setup?: MetaResolveSetup;
resolve: ResolveMethod;
};
/**
* The meta manager
*/
declare type MetaManager = {
readonly config: MetaConfig;
install(app: App): void;
addMeta(source: MetaSource, vm?: ComponentInternalInstance): MetaProxy;
render(ctx?: {
slots?: Slots;
}): Array<VNode>;
};
/**
* @internal
*/
declare type MetaTeleports = {
[key: string]: Array<VNode>;
};
/**
* @internal
*/
interface MetaRenderContext {
slots?: Slots;
metainfo: MetaActive;
}
/**
* @internal
*/
interface MetaGroupConfig {
group: string;
data: Array<TODO> | TODO;
tagNamespace?: string;
fullName?: string;
slotName?: string;
}
/**
* @internal
*/
interface SlotScopeProperties {
content: string;
metainfo: MetaActive;
[key: string]: any;
}
/**
* @internal
*/
declare type MetaRenderedNode = {
vnode: VNode;
to?: string;
};
/**
* @internal
*/
declare type MetaRendered = Array<MetaRenderedNode>;
declare module '@vue/runtime-core' {
interface ComponentInternalInstance {
$metaManager: MetaManager;
}
}
declare type MergeResolveContextDeepest = MetaResolveContext & {
depth: number;
};
declare function setup(context: MergeResolveContextDeepest): void;
declare const resolve: ResolveMethod;
declare const deepest_d_setup: typeof setup;
declare const deepest_d_resolve: typeof resolve;
declare namespace deepest_d {
export {
deepest_d_setup as setup,
deepest_d_resolve as resolve,
};
}
declare const defaultConfig: MetaConfig;
declare function createMetaManager(config: MetaConfig, resolver: MetaResolver | ResolveMethod): MetaManager;
declare type ResolveOptionReducer = (accumulator: any, context: ResolveContext) => ResolveMethod;
declare const resolveOption: (predicament: ResolveOptionReducer) => ResolveMethod;
declare function renderToStringWithMeta(app: App): Promise<[string, SSRContext]>;
declare function getCurrentManager(vm?: ComponentInternalInstance): MetaManager | undefined;
declare function useMeta(source: MetaSource, manager?: MetaManager): MetaProxy;
declare function useActiveMeta(): MetaActive;
export { MetaActive, MetaConfig, MetaConfigSection, MetaConfigSectionAttribute, MetaConfigSectionGroup, MetaConfigSectionKey, MetaConfigSectionTag, MetaGroupConfig, MetaManager, MetaProxy, MetaRenderContext, MetaRendered, MetaRenderedNode, MetaResolveContext, MetaResolveSetup, MetaResolver, MetaSource, MetaSourceProxy, MetaTagConfig, MetaTagConfigKey, MetaTagName, MetaTagsConfig, MetaTeleports, SlotScopeProperties, TODO, createMetaManager, deepest_d as deepestResolver, defaultConfig, getCurrentManager, renderToStringWithMeta, resolveOption, useActiveMeta, useMeta };
+118 -111
View File
@@ -87,29 +87,8 @@ const defaultConfig = {
} }
}; };
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
/* /*
* This is a fixed config for real HTML tags * This is a fixed config for real HTML tags
*
* TODO: we probably dont need all attributes
*/ */
const tags = { const tags = {
title: { title: {
@@ -162,24 +141,34 @@ const tags = {
} }
}; };
function getConfigByKey(tagOrName, key, config) { function getTagConfigItem(tagOrName, key) {
if (config && key in config) { for (const name of tagOrName) {
return config[key]; const tag = tags[name];
} if (name && tag) {
if (isArray(tagOrName)) { return tag[key];
for (const name of tagOrName) {
if (name && name in tags) {
return tags[name][key];
}
} }
return;
}
if (tagOrName in tags) {
const tag = tags[tagOrName];
return tag[key];
} }
} }
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
// https://github.com/microsoft/TypeScript/issues/1863 // https://github.com/microsoft/TypeScript/issues/1863
const IS_PROXY = Symbol('kIsProxy'); const IS_PROXY = Symbol('kIsProxy');
const PROXY_SOURCES = Symbol('kProxySources'); const PROXY_SOURCES = Symbol('kProxySources');
@@ -439,42 +428,37 @@ const createMergedObject = (resolve, active = {}) => {
sources sources
}; };
const compute = () => recompute(context); const compute = () => recompute(context);
const addSource = (source, resolveContext, recompute = false) => { return {
const proxy = createProxy(context, source, resolveContext || {}); context,
if (recompute) { compute,
compute(); addSource: (source, resolveContext, recompute = false) => {
} const proxy = createProxy(context, source, resolveContext || {});
return proxy;
};
const delSource = (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) { if (recompute) {
compute(); compute();
} }
return true; return proxy;
},
delSource: (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) {
compute();
}
return true;
}
return false;
} }
return false;
};
return {
context,
active,
resolve,
sources,
addSource,
delSource,
compute
}; };
}; };
const cachedElements = {}; 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 (config.attributesFor) { if ('attributesFor' in config) {
return renderAttributes(context, key, data, config); return renderAttributes(context, key, data, config);
} }
if (config.group) { if ('group' in config) {
return renderGroup(context, key, data, config); return renderGroup(context, key, data, config);
} }
return renderTag(context, key, data, config); return renderTag(context, key, data, config);
@@ -484,7 +468,7 @@ function renderGroup(context, key, data, config) {
if (isArray(data)) { if (isArray(data)) {
{ {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Specifying an array for group properties isnt supported mostly as we didnt found a use-case for this yet. If you have one, please create an issue on the vue-meta repo'); console.warn('Specifying an array for group properties isnt supported');
} }
// config.attributes = getConfigKey([key, config.tag], 'attributes', config) // config.attributes = getConfigKey([key, config.tag], 'attributes', config)
return []; return [];
@@ -510,7 +494,7 @@ function renderGroup(context, key, data, config) {
function renderTag(context, key, data, config = {}, groupConfig) { function renderTag(context, key, data, config = {}, groupConfig) {
// console.info('renderTag', key, data, config, groupConfig) // console.info('renderTag', key, data, config, groupConfig)
const contentAttributes = ['content', 'json', 'rawContent']; const contentAttributes = ['content', 'json', 'rawContent'];
const getConfig = (key) => getConfigByKey([tag, config.tag], key, config); const getTagConfig = (key) => getTagConfigItem([tag, config.tag], key);
if (isArray(data)) { if (isArray(data)) {
return data return data
.map((child) => { .map((child) => {
@@ -519,7 +503,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
.flat(); .flat();
} }
const { tag = config.tag || key } = data; const { tag = config.tag || key } = data;
let content; let content = '';
let hasChilds = false; let hasChilds = false;
let isRaw = false; let isRaw = false;
if (isString(data)) { if (isString(data)) {
@@ -571,24 +555,24 @@ function renderTag(context, key, data, config = {}, groupConfig) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
const contentAsAttribute = getConfig('contentAsAttribute'); const contentAsAttribute = !!getTagConfig('contentAsAttribute');
let valueAttribute = config.valueAttribute; let { valueAttribute } = config;
if (!valueAttribute && contentAsAttribute) { if (!valueAttribute && contentAsAttribute) {
const tagAttributes = getConfig('attributes'); const [tagAttribute] = getTagConfig('attributes');
valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute;
} }
if (!valueAttribute) { if (!valueAttribute) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
if (!config.nameless) { const { nameless, keyAttribute } = config;
const keyAttribute = getConfig('keyAttribute'); if (!nameless) {
if (keyAttribute) { if (keyAttribute) {
attributes[keyAttribute] = fullName; attributes[keyAttribute] = fullName;
} }
} }
attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig);
content = undefined; content = '';
} }
} }
const finalTag = groupConfig && groupConfig.tagNamespace const finalTag = groupConfig && groupConfig.tagNamespace
@@ -598,32 +582,32 @@ function renderTag(context, key, data, config = {}, groupConfig) {
// 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)
let vnode; if (isRaw && content) {
if (isRaw) {
attributes.innerHTML = content; attributes.innerHTML = content;
vnode = h(finalTag, attributes);
}
else {
vnode = h(finalTag, attributes, content);
} }
// Ignore empty string content
const vnode = h(finalTag, attributes, content || undefined);
return { return {
to: data.to, to: data.to,
vnode vnode
}; };
} }
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) {
return;
}
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) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('Could not find element with selector', attributesFor, ', won\'t render attributes'); console.error('Could not find element for selector', attributesFor, ', won\'t render attributes');
return; return;
} }
if ( el2) { if ( el2) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Found multiple elements with selector', attributesFor); console.warn('Found multiple elements for selector', attributesFor);
} }
cachedElements[attributesFor] = { cachedElements[attributesFor] = {
el, el,
@@ -633,7 +617,7 @@ 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); const content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
el.setAttribute(attr, `${content || ''}`); el.setAttribute(attr, content || '');
if (!attrs.includes(attr)) { if (!attrs.includes(attr)) {
attrs.push(attr); attrs.push(attr);
} }
@@ -644,19 +628,22 @@ function renderAttributes(context, key, data, config = {}) {
} }
} }
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
if (!slots || !slots[slotName]) { const slot = slots && slots[slotName];
if (!slot) {
return content; return content;
} }
const slotProps = { const slotScopeProps = {
content, content,
metainfo metainfo
}; };
if (groupConfig && groupConfig.group) { if (groupConfig && groupConfig.group) {
slotProps[groupConfig.group] = groupConfig.data; const { group, data } = groupConfig;
slotScopeProps[group] = data;
} }
const slotContent = slots[slotName](slotProps); const slotContent = slot(slotScopeProps);
if (slotContent && slotContent.length) { if (slotContent && slotContent.length) {
return slotContent[0].children; const { children } = slotContent[0];
return children ? children.toString() : '';
} }
return content; return content;
} }
@@ -667,15 +654,18 @@ const PolySymbol = (name) =>
hasSymbol hasSymbol
? Symbol( '[vue-meta]: ' + name ) ? Symbol( '[vue-meta]: ' + name )
: ( '[vue-meta]: ' ) + name; : ( '[vue-meta]: ' ) + name;
const metaInfoKey = PolySymbol( 'metainfo' ); const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
function getCurrentManager(vm) { function getCurrentManager(vm) {
if (!vm) { if (!vm) {
vm = getCurrentInstance(); vm = getCurrentInstance() || undefined;
}
if (!vm) {
return undefined;
} }
return vm.appContext.config.globalProperties.$metaManager; return vm.appContext.config.globalProperties.$metaManager;
} }
function useMeta(obj, manager) { function useMeta(source, manager) {
const vm = getCurrentInstance(); const vm = getCurrentInstance();
if (!manager && vm) { if (!manager && vm) {
manager = getCurrentManager(vm); manager = getCurrentManager(vm);
@@ -684,10 +674,10 @@ function useMeta(obj, manager) {
// oopsydoopsy // oopsydoopsy
throw new Error('No manager or current instance'); throw new Error('No manager or current instance');
} }
return manager.addMeta(obj, vm || undefined); return manager.addMeta(source, vm || undefined);
} }
function useMetainfo() { function useActiveMeta() {
return inject(metaInfoKey); return inject(metaActiveKey);
} }
const MetainfoImpl = defineComponent({ const MetainfoImpl = defineComponent({
@@ -707,22 +697,24 @@ 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(teleports, to, vnodes) {
const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
{ {
// 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
vnodes.forEach((vnode, idx) => { nodes.forEach((vnode, idx) => {
if (vnode.type === Comment) { if (vnode.type === Comment) {
vnodes.splice(idx, 1); nodes.splice(idx, 1);
} }
}); });
// only add ssrAttribute's for real meta tags
} }
if (!teleports[to]) { if (!teleports[to]) {
teleports[to] = []; teleports[to] = [];
} }
teleports[to].push(...vnodes); teleports[to].push(...nodes);
} }
function createMetaManager(config, resolver) { function createMetaManager(config, resolver) {
let cleanedUpSsr = false;
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);
@@ -730,16 +722,18 @@ function createMetaManager(config, resolver) {
return resolver.resolve(options, contexts, active, key, pathSegments); return resolver.resolve(options, contexts, active, key, pathSegments);
}; };
const { addSource, delSource } = createMergedObject(resolve, active); const { addSource, delSource } = createMergedObject(resolve, active);
let cleanedUpSsr = false;
// TODO: validate resolver // TODO: validate resolver
const manager = { const manager = {
config, config,
install(app) { install(app) {
app.component('Metainfo', Metainfo); app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager; app.config.globalProperties.$metaManager = manager;
app.provide(metaInfoKey, active); app.provide(metaActiveKey, active);
}, },
addMeta(metaObj, vm) { addMeta(metaObj, vm) {
if (!vm) {
vm = getCurrentInstance() || undefined;
}
const resolveContext = { vm }; const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext); resolver.setup(resolveContext);
@@ -759,9 +753,9 @@ function createMetaManager(config, resolver) {
// cleanup ssr tags if not yet done // cleanup ssr tags if not yet done
if ( !cleanedUpSsr) { if ( !cleanedUpSsr) {
cleanedUpSsr = true; cleanedUpSsr = true;
// Listen for DOM loaded because tags in the body couldnt be loaded // Listen for DOM loaded because tags in the body couldnt
// yet once the manager does it first render // have loaded yet once the manager does it first render
// (preferable there should only be one render on hydration) // (preferable there should only be one meta render on hydration)
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`); const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
if (ssrTags && ssrTags.length) { if (ssrTags && ssrTags.length) {
@@ -772,21 +766,34 @@ function createMetaManager(config, resolver) {
const teleports = {}; const teleports = {};
for (const key in active) { for (const key in active) {
const config = this.config[key] || {}; const config = this.config[key] || {};
const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!vnode) { if (!renderedNodes) {
continue; continue;
} }
const vnodes = isArray(vnode) ? vnode : [vnode]; if (!isArray(renderedNodes)) {
const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); renderedNodes = [renderedNodes];
for (const { to, vnode } of vnodes) { }
addVnode(teleports, to || defaultTo, vnode); 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) { if (slots) {
for (const tag in slots) { for (const slotName in slots) {
const slotFn = slots[tag]; const tagName = slotName === 'default' ? 'head' : slotName;
if (isFunction(slotFn)) { // Only teleport the contents of head/body slots
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
} }
} }
} }
@@ -798,4 +805,4 @@ function createMetaManager(config, resolver) {
return manager; return manager;
} }
export { createMetaManager, deepest as deepestResolver, defaultConfig, getCurrentManager, resolveOption, useMeta, useMetainfo }; export { createMetaManager, deepest as deepestResolver, defaultConfig, getCurrentManager, resolveOption, useActiveMeta, useMeta };
+1 -1
View File
File diff suppressed because one or more lines are too long
+116 -113
View File
@@ -87,29 +87,8 @@ const defaultConfig = {
} }
}; };
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
/* /*
* This is a fixed config for real HTML tags * This is a fixed config for real HTML tags
*
* TODO: we probably dont need all attributes
*/ */
const tags = { const tags = {
title: { title: {
@@ -162,24 +141,34 @@ const tags = {
} }
}; };
function getConfigByKey(tagOrName, key, config) { function getTagConfigItem(tagOrName, key) {
if (config && key in config) { for (const name of tagOrName) {
return config[key]; const tag = tags[name];
} if (name && tag) {
if (isArray(tagOrName)) { return tag[key];
for (const name of tagOrName) {
if (name && name in tags) {
return tags[name][key];
}
} }
return;
}
if (tagOrName in tags) {
const tag = tags[tagOrName];
return tag[key];
} }
} }
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
// https://github.com/microsoft/TypeScript/issues/1863 // https://github.com/microsoft/TypeScript/issues/1863
const IS_PROXY = Symbol('kIsProxy'); const IS_PROXY = Symbol('kIsProxy');
const PROXY_SOURCES = Symbol('kProxySources'); const PROXY_SOURCES = Symbol('kProxySources');
@@ -439,41 +428,36 @@ const createMergedObject = (resolve, active = {}) => {
sources sources
}; };
const compute = () => recompute(context); const compute = () => recompute(context);
const addSource = (source, resolveContext, recompute = false) => { return {
const proxy = createProxy(context, source, resolveContext || {}); context,
if (recompute) { compute,
compute(); addSource: (source, resolveContext, recompute = false) => {
} const proxy = createProxy(context, source, resolveContext || {});
return proxy;
};
const delSource = (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) { if (recompute) {
compute(); compute();
} }
return true; return proxy;
},
delSource: (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) {
compute();
}
return true;
}
return false;
} }
return false;
};
return {
context,
active,
resolve,
sources,
addSource,
delSource,
compute
}; };
}; };
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 (config.attributesFor) { if ('attributesFor' in config) {
return renderAttributes(context, key, data, config); return renderAttributes(context, key, data, config);
} }
if (config.group) { if ('group' in config) {
return renderGroup(context, key, data, config); return renderGroup(context, key, data, config);
} }
return renderTag(context, key, data, config); return renderTag(context, key, data, config);
@@ -483,7 +467,7 @@ function renderGroup(context, key, data, config) {
if (isArray(data)) { if (isArray(data)) {
if ((process.env.NODE_ENV !== 'production')) { if ((process.env.NODE_ENV !== 'production')) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Specifying an array for group properties isnt supported mostly as we didnt found a use-case for this yet. If you have one, please create an issue on the vue-meta repo'); console.warn('Specifying an array for group properties isnt supported');
} }
// config.attributes = getConfigKey([key, config.tag], 'attributes', config) // config.attributes = getConfigKey([key, config.tag], 'attributes', config)
return []; return [];
@@ -509,7 +493,7 @@ function renderGroup(context, key, data, config) {
function renderTag(context, key, data, config = {}, groupConfig) { function renderTag(context, key, data, config = {}, groupConfig) {
// console.info('renderTag', key, data, config, groupConfig) // console.info('renderTag', key, data, config, groupConfig)
const contentAttributes = ['content', 'json', 'rawContent']; const contentAttributes = ['content', 'json', 'rawContent'];
const getConfig = (key) => getConfigByKey([tag, config.tag], key, config); const getTagConfig = (key) => getTagConfigItem([tag, config.tag], key);
if (isArray(data)) { if (isArray(data)) {
return data return data
.map((child) => { .map((child) => {
@@ -518,7 +502,7 @@ function renderTag(context, key, data, config = {}, groupConfig) {
.flat(); .flat();
} }
const { tag = config.tag || key } = data; const { tag = config.tag || key } = data;
let content; let content = '';
let hasChilds = false; let hasChilds = false;
let isRaw = false; let isRaw = false;
if (isString(data)) { if (isString(data)) {
@@ -570,24 +554,24 @@ function renderTag(context, key, data, config = {}, groupConfig) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
const contentAsAttribute = getConfig('contentAsAttribute'); const contentAsAttribute = !!getTagConfig('contentAsAttribute');
let valueAttribute = config.valueAttribute; let { valueAttribute } = config;
if (!valueAttribute && contentAsAttribute) { if (!valueAttribute && contentAsAttribute) {
const tagAttributes = getConfig('attributes'); const [tagAttribute] = getTagConfig('attributes');
valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute;
} }
if (!valueAttribute) { if (!valueAttribute) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
if (!config.nameless) { const { nameless, keyAttribute } = config;
const keyAttribute = getConfig('keyAttribute'); if (!nameless) {
if (keyAttribute) { if (keyAttribute) {
attributes[keyAttribute] = fullName; attributes[keyAttribute] = fullName;
} }
} }
attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig);
content = undefined; content = '';
} }
} }
const finalTag = groupConfig && groupConfig.tagNamespace const finalTag = groupConfig && groupConfig.tagNamespace
@@ -597,22 +581,22 @@ function renderTag(context, key, data, config = {}, groupConfig) {
// 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)
let vnode; if (isRaw && content) {
if (isRaw) {
attributes.innerHTML = content; attributes.innerHTML = content;
vnode = h(finalTag, attributes);
}
else {
vnode = h(finalTag, attributes, content);
} }
// Ignore empty string content
const vnode = h(finalTag, attributes, content || undefined);
return { return {
to: data.to, to: data.to,
vnode vnode
}; };
} }
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) {
return;
}
{ {
// 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
@@ -623,19 +607,22 @@ function renderAttributes(context, key, data, config = {}) {
} }
} }
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
if (!slots || !slots[slotName]) { const slot = slots && slots[slotName];
if (!slot) {
return content; return content;
} }
const slotProps = { const slotScopeProps = {
content, content,
metainfo metainfo
}; };
if (groupConfig && groupConfig.group) { if (groupConfig && groupConfig.group) {
slotProps[groupConfig.group] = groupConfig.data; const { group, data } = groupConfig;
slotScopeProps[group] = data;
} }
const slotContent = slots[slotName](slotProps); const slotContent = slot(slotScopeProps);
if (slotContent && slotContent.length) { if (slotContent && slotContent.length) {
return slotContent[0].children; const { children } = slotContent[0];
return children ? children.toString() : '';
} }
return content; return content;
} }
@@ -646,15 +633,18 @@ const PolySymbol = (name) =>
hasSymbol hasSymbol
? Symbol((process.env.NODE_ENV !== 'production') ? '[vue-meta]: ' + name : name) ? Symbol((process.env.NODE_ENV !== 'production') ? '[vue-meta]: ' + name : name)
: ((process.env.NODE_ENV !== 'production') ? '[vue-meta]: ' : '_vm_') + name; : ((process.env.NODE_ENV !== 'production') ? '[vue-meta]: ' : '_vm_') + name;
const metaInfoKey = PolySymbol((process.env.NODE_ENV !== 'production') ? 'metainfo' : 'mi'); const metaActiveKey = /*#__PURE__*/ PolySymbol((process.env.NODE_ENV !== 'production') ? 'active_meta' : 'am');
function getCurrentManager(vm) { function getCurrentManager(vm) {
if (!vm) { if (!vm) {
vm = getCurrentInstance(); vm = getCurrentInstance() || undefined;
}
if (!vm) {
return undefined;
} }
return vm.appContext.config.globalProperties.$metaManager; return vm.appContext.config.globalProperties.$metaManager;
} }
function useMeta(obj, manager) { function useMeta(source, manager) {
const vm = getCurrentInstance(); const vm = getCurrentInstance();
if (!manager && vm) { if (!manager && vm) {
manager = getCurrentManager(vm); manager = getCurrentManager(vm);
@@ -663,10 +653,10 @@ function useMeta(obj, manager) {
// oopsydoopsy // oopsydoopsy
throw new Error('No manager or current instance'); throw new Error('No manager or current instance');
} }
return manager.addMeta(obj, vm || undefined); return manager.addMeta(source, vm || undefined);
} }
function useMetainfo() { function useActiveMeta() {
return inject(metaInfoKey); return inject(metaActiveKey);
} }
const MetainfoImpl = defineComponent({ const MetainfoImpl = defineComponent({
@@ -686,23 +676,20 @@ 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(teleports, to, vnodes) {
const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
{ if (!to.endsWith('Attrs')) {
// dont add ssrAttribute for attribute vnode placeholder nodes.forEach((vnode) => {
if (!to.endsWith('Attrs')) { if (!vnode.props) {
vnodes.forEach((vnode) => { vnode.props = {};
if (!vnode.props) { }
vnode.props = {}; vnode.props[ssrAttribute] = true;
} });
vnode.props[ssrAttribute] = true;
});
}
} }
if (!teleports[to]) { if (!teleports[to]) {
teleports[to] = []; teleports[to] = [];
} }
teleports[to].push(...vnodes); teleports[to].push(...nodes);
} }
function createMetaManager(config, resolver) { function createMetaManager(config, resolver) {
const resolve = (options, contexts, active, key, pathSegments) => { const resolve = (options, contexts, active, key, pathSegments) => {
@@ -718,9 +705,12 @@ function createMetaManager(config, resolver) {
install(app) { install(app) {
app.component('Metainfo', Metainfo); app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager; app.config.globalProperties.$metaManager = manager;
app.provide(metaInfoKey, active); app.provide(metaActiveKey, active);
}, },
addMeta(metaObj, vm) { addMeta(metaObj, vm) {
if (!vm) {
vm = getCurrentInstance() || undefined;
}
const resolveContext = { vm }; const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext); resolver.setup(resolveContext);
@@ -740,21 +730,34 @@ function createMetaManager(config, resolver) {
const teleports = {}; const teleports = {};
for (const key in active) { for (const key in active) {
const config = this.config[key] || {}; const config = this.config[key] || {};
const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!vnode) { if (!renderedNodes) {
continue; continue;
} }
const vnodes = isArray(vnode) ? vnode : [vnode]; if (!isArray(renderedNodes)) {
const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); renderedNodes = [renderedNodes];
for (const { to, vnode } of vnodes) { }
addVnode(teleports, to || defaultTo, vnode); 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) { if (slots) {
for (const tag in slots) { for (const slotName in slots) {
const slotFn = slots[tag]; const tagName = slotName === 'default' ? 'head' : slotName;
if (isFunction(slotFn)) { // Only teleport the contents of head/body slots
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
} }
} }
} }
@@ -766,7 +769,7 @@ function createMetaManager(config, resolver) {
return manager; return manager;
} }
// rollup doesnt like an import, cant find export so use require // rollup doesnt like an import as it cant find the export so use require
const { renderToString } = require('@vue/server-renderer'); const { renderToString } = require('@vue/server-renderer');
async function renderToStringWithMeta(app) { async function renderToStringWithMeta(app) {
const ctx = {}; const ctx = {};
@@ -787,4 +790,4 @@ async function renderToStringWithMeta(app) {
return [html, ctx]; return [html, ctx];
} }
export { createMetaManager, deepest as deepestResolver, defaultConfig, getCurrentManager, renderToStringWithMeta, resolveOption, useMeta, useMetainfo }; export { createMetaManager, deepest as deepestResolver, defaultConfig, getCurrentManager, renderToStringWithMeta, resolveOption, useActiveMeta, useMeta };
+118 -111
View File
@@ -88,29 +88,8 @@ var VueMeta = (function (exports, vue) {
} }
}; };
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
/* /*
* This is a fixed config for real HTML tags * This is a fixed config for real HTML tags
*
* TODO: we probably dont need all attributes
*/ */
const tags = { const tags = {
title: { title: {
@@ -163,24 +142,34 @@ var VueMeta = (function (exports, vue) {
} }
}; };
function getConfigByKey(tagOrName, key, config) { function getTagConfigItem(tagOrName, key) {
if (config && key in config) { for (const name of tagOrName) {
return config[key]; const tag = tags[name];
} if (name && tag) {
if (isArray(tagOrName)) { return tag[key];
for (const name of tagOrName) {
if (name && name in tags) {
return tags[name][key];
}
} }
return;
}
if (tagOrName in tags) {
const tag = tags[tagOrName];
return tag[key];
} }
} }
/**
* Make a map and return a function for checking if a key
* is in that map.
* IMPORTANT: all calls of this function must be prefixed with
* \/\*#\_\_PURE\_\_\*\/
* So that rollup can tree-shake them if necessary.
*/
(process.env.NODE_ENV !== 'production')
? Object.freeze({})
: {};
(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isObject = (val) => val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
// https://github.com/microsoft/TypeScript/issues/1863 // https://github.com/microsoft/TypeScript/issues/1863
const IS_PROXY = Symbol('kIsProxy'); const IS_PROXY = Symbol('kIsProxy');
const PROXY_SOURCES = Symbol('kProxySources'); const PROXY_SOURCES = Symbol('kProxySources');
@@ -440,42 +429,37 @@ var VueMeta = (function (exports, vue) {
sources sources
}; };
const compute = () => recompute(context); const compute = () => recompute(context);
const addSource = (source, resolveContext, recompute = false) => { return {
const proxy = createProxy(context, source, resolveContext || {}); context,
if (recompute) { compute,
compute(); addSource: (source, resolveContext, recompute = false) => {
} const proxy = createProxy(context, source, resolveContext || {});
return proxy;
};
const delSource = (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) { if (recompute) {
compute(); compute();
} }
return true; return proxy;
},
delSource: (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy);
if (index > -1) {
sources.splice(index, 1);
if (recompute) {
compute();
}
return true;
}
return false;
} }
return false;
};
return {
context,
active,
resolve,
sources,
addSource,
delSource,
compute
}; };
}; };
const cachedElements = {}; 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 (config.attributesFor) { if ('attributesFor' in config) {
return renderAttributes(context, key, data, config); return renderAttributes(context, key, data, config);
} }
if (config.group) { if ('group' in config) {
return renderGroup(context, key, data, config); return renderGroup(context, key, data, config);
} }
return renderTag(context, key, data, config); return renderTag(context, key, data, config);
@@ -485,7 +469,7 @@ var VueMeta = (function (exports, vue) {
if (isArray(data)) { if (isArray(data)) {
{ {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Specifying an array for group properties isnt supported mostly as we didnt found a use-case for this yet. If you have one, please create an issue on the vue-meta repo'); console.warn('Specifying an array for group properties isnt supported');
} }
// config.attributes = getConfigKey([key, config.tag], 'attributes', config) // config.attributes = getConfigKey([key, config.tag], 'attributes', config)
return []; return [];
@@ -511,7 +495,7 @@ var VueMeta = (function (exports, vue) {
function renderTag(context, key, data, config = {}, groupConfig) { function renderTag(context, key, data, config = {}, groupConfig) {
// console.info('renderTag', key, data, config, groupConfig) // console.info('renderTag', key, data, config, groupConfig)
const contentAttributes = ['content', 'json', 'rawContent']; const contentAttributes = ['content', 'json', 'rawContent'];
const getConfig = (key) => getConfigByKey([tag, config.tag], key, config); const getTagConfig = (key) => getTagConfigItem([tag, config.tag], key);
if (isArray(data)) { if (isArray(data)) {
return data return data
.map((child) => { .map((child) => {
@@ -520,7 +504,7 @@ var VueMeta = (function (exports, vue) {
.flat(); .flat();
} }
const { tag = config.tag || key } = data; const { tag = config.tag || key } = data;
let content; let content = '';
let hasChilds = false; let hasChilds = false;
let isRaw = false; let isRaw = false;
if (isString(data)) { if (isString(data)) {
@@ -572,24 +556,24 @@ var VueMeta = (function (exports, vue) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
const contentAsAttribute = getConfig('contentAsAttribute'); const contentAsAttribute = !!getTagConfig('contentAsAttribute');
let valueAttribute = config.valueAttribute; let { valueAttribute } = config;
if (!valueAttribute && contentAsAttribute) { if (!valueAttribute && contentAsAttribute) {
const tagAttributes = getConfig('attributes'); const [tagAttribute] = getTagConfig('attributes');
valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute;
} }
if (!valueAttribute) { if (!valueAttribute) {
content = getSlotContent(context, slotName, content, data); content = getSlotContent(context, slotName, content, data);
} }
else { else {
if (!config.nameless) { const { nameless, keyAttribute } = config;
const keyAttribute = getConfig('keyAttribute'); if (!nameless) {
if (keyAttribute) { if (keyAttribute) {
attributes[keyAttribute] = fullName; attributes[keyAttribute] = fullName;
} }
} }
attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig);
content = undefined; content = '';
} }
} }
const finalTag = groupConfig && groupConfig.tagNamespace const finalTag = groupConfig && groupConfig.tagNamespace
@@ -599,32 +583,32 @@ var VueMeta = (function (exports, vue) {
// 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)
let vnode; if (isRaw && content) {
if (isRaw) {
attributes.innerHTML = content; attributes.innerHTML = content;
vnode = vue.h(finalTag, attributes);
}
else {
vnode = vue.h(finalTag, attributes, content);
} }
// Ignore empty string content
const vnode = vue.h(finalTag, attributes, content || undefined);
return { return {
to: data.to, to: data.to,
vnode vnode
}; };
} }
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) {
return;
}
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) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('Could not find element with selector', attributesFor, ', won\'t render attributes'); console.error('Could not find element for selector', attributesFor, ', won\'t render attributes');
return; return;
} }
if ( el2) { if ( el2) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Found multiple elements with selector', attributesFor); console.warn('Found multiple elements for selector', attributesFor);
} }
cachedElements[attributesFor] = { cachedElements[attributesFor] = {
el, el,
@@ -634,7 +618,7 @@ 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); const content = getSlotContent(context, `${key}(${attr})`, data[attr], data);
el.setAttribute(attr, `${content || ''}`); el.setAttribute(attr, content || '');
if (!attrs.includes(attr)) { if (!attrs.includes(attr)) {
attrs.push(attr); attrs.push(attr);
} }
@@ -645,19 +629,22 @@ var VueMeta = (function (exports, vue) {
} }
} }
function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) {
if (!slots || !slots[slotName]) { const slot = slots && slots[slotName];
if (!slot) {
return content; return content;
} }
const slotProps = { const slotScopeProps = {
content, content,
metainfo metainfo
}; };
if (groupConfig && groupConfig.group) { if (groupConfig && groupConfig.group) {
slotProps[groupConfig.group] = groupConfig.data; const { group, data } = groupConfig;
slotScopeProps[group] = data;
} }
const slotContent = slots[slotName](slotProps); const slotContent = slot(slotScopeProps);
if (slotContent && slotContent.length) { if (slotContent && slotContent.length) {
return slotContent[0].children; const { children } = slotContent[0];
return children ? children.toString() : '';
} }
return content; return content;
} }
@@ -668,15 +655,18 @@ var VueMeta = (function (exports, vue) {
hasSymbol hasSymbol
? Symbol( '[vue-meta]: ' + name ) ? Symbol( '[vue-meta]: ' + name )
: ( '[vue-meta]: ' ) + name; : ( '[vue-meta]: ' ) + name;
const metaInfoKey = PolySymbol( 'metainfo' ); const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' );
function getCurrentManager(vm) { function getCurrentManager(vm) {
if (!vm) { if (!vm) {
vm = vue.getCurrentInstance(); vm = vue.getCurrentInstance() || undefined;
}
if (!vm) {
return undefined;
} }
return vm.appContext.config.globalProperties.$metaManager; return vm.appContext.config.globalProperties.$metaManager;
} }
function useMeta(obj, manager) { function useMeta(source, manager) {
const vm = vue.getCurrentInstance(); const vm = vue.getCurrentInstance();
if (!manager && vm) { if (!manager && vm) {
manager = getCurrentManager(vm); manager = getCurrentManager(vm);
@@ -685,10 +675,10 @@ var VueMeta = (function (exports, vue) {
// oopsydoopsy // oopsydoopsy
throw new Error('No manager or current instance'); throw new Error('No manager or current instance');
} }
return manager.addMeta(obj, vm || undefined); return manager.addMeta(source, vm || undefined);
} }
function useMetainfo() { function useActiveMeta() {
return vue.inject(metaInfoKey); return vue.inject(metaActiveKey);
} }
const MetainfoImpl = vue.defineComponent({ const MetainfoImpl = vue.defineComponent({
@@ -708,22 +698,24 @@ 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(teleports, to, vnodes) {
const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); const nodes = (isArray(vnodes) ? vnodes : [vnodes]);
{ {
// 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
vnodes.forEach((vnode, idx) => { nodes.forEach((vnode, idx) => {
if (vnode.type === vue.Comment) { if (vnode.type === vue.Comment) {
vnodes.splice(idx, 1); nodes.splice(idx, 1);
} }
}); });
// only add ssrAttribute's for real meta tags
} }
if (!teleports[to]) { if (!teleports[to]) {
teleports[to] = []; teleports[to] = [];
} }
teleports[to].push(...vnodes); teleports[to].push(...nodes);
} }
function createMetaManager(config, resolver) { function createMetaManager(config, resolver) {
let cleanedUpSsr = false;
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);
@@ -731,16 +723,18 @@ var VueMeta = (function (exports, vue) {
return resolver.resolve(options, contexts, active, key, pathSegments); return resolver.resolve(options, contexts, active, key, pathSegments);
}; };
const { addSource, delSource } = createMergedObject(resolve, active); const { addSource, delSource } = createMergedObject(resolve, active);
let cleanedUpSsr = false;
// TODO: validate resolver // TODO: validate resolver
const manager = { const manager = {
config, config,
install(app) { install(app) {
app.component('Metainfo', Metainfo); app.component('Metainfo', Metainfo);
app.config.globalProperties.$metaManager = manager; app.config.globalProperties.$metaManager = manager;
app.provide(metaInfoKey, active); app.provide(metaActiveKey, active);
}, },
addMeta(metaObj, vm) { addMeta(metaObj, vm) {
if (!vm) {
vm = vue.getCurrentInstance() || undefined;
}
const resolveContext = { vm }; const resolveContext = { vm };
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext); resolver.setup(resolveContext);
@@ -760,9 +754,9 @@ var VueMeta = (function (exports, vue) {
// cleanup ssr tags if not yet done // cleanup ssr tags if not yet done
if ( !cleanedUpSsr) { if ( !cleanedUpSsr) {
cleanedUpSsr = true; cleanedUpSsr = true;
// Listen for DOM loaded because tags in the body couldnt be loaded // Listen for DOM loaded because tags in the body couldnt
// yet once the manager does it first render // have loaded yet once the manager does it first render
// (preferable there should only be one render on hydration) // (preferable there should only be one meta render on hydration)
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`); const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`);
if (ssrTags && ssrTags.length) { if (ssrTags && ssrTags.length) {
@@ -773,21 +767,34 @@ var VueMeta = (function (exports, vue) {
const teleports = {}; const teleports = {};
for (const key in active) { for (const key in active) {
const config = this.config[key] || {}; const config = this.config[key] || {};
const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config);
if (!vnode) { if (!renderedNodes) {
continue; continue;
} }
const vnodes = isArray(vnode) ? vnode : [vnode]; if (!isArray(renderedNodes)) {
const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); renderedNodes = [renderedNodes];
for (const { to, vnode } of vnodes) { }
addVnode(teleports, to || defaultTo, vnode); 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) { if (slots) {
for (const tag in slots) { for (const slotName in slots) {
const slotFn = slots[tag]; const tagName = slotName === 'default' ? 'head' : slotName;
if (isFunction(slotFn)) { // Only teleport the contents of head/body slots
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); if (tagName !== 'head' && tagName !== 'body') {
continue;
}
const slot = slots[slotName];
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }));
} }
} }
} }
@@ -804,8 +811,8 @@ var VueMeta = (function (exports, vue) {
exports.defaultConfig = defaultConfig; exports.defaultConfig = defaultConfig;
exports.getCurrentManager = getCurrentManager; exports.getCurrentManager = getCurrentManager;
exports.resolveOption = resolveOption; exports.resolveOption = resolveOption;
exports.useActiveMeta = useActiveMeta;
exports.useMeta = useMeta; exports.useMeta = useMeta;
exports.useMetainfo = useMetainfo;
Object.defineProperty(exports, '__esModule', { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
+1 -1
View File
File diff suppressed because one or more lines are too long
+28 -5
View File
@@ -1,5 +1,5 @@
import { watch } from 'vue' import { watch } from 'vue'
import { useMeta, useMetainfo } from 'vue-meta' import { useMeta, useActiveMeta } from 'vue-meta'
export default { export default {
setup () { setup () {
@@ -89,7 +89,7 @@ export default {
setTimeout(() => (meta.title = 'My Updated Title'), 2000) setTimeout(() => (meta.title = 'My Updated Title'), 2000)
setTimeout(() => (meta.htmlAttrs.amp = undefined), 2000) setTimeout(() => (meta.htmlAttrs.amp = undefined), 2000)
const metadata = useMetainfo() const metadata = useActiveMeta()
if (!process.server) { if (!process.server) {
window.$metadata = metadata window.$metadata = metadata
@@ -122,8 +122,7 @@ export default {
{{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again {{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again
</template> </template>
<!-- // TODO: Using script triggers [Vue warn]: Template compilation error: Tags with side effect (<script> and <style>) are i <!-- // TODO: Using script triggers [Vue warn]: Template compilation error: Tags with side effect (<script> and <style>) are ignored in client component templates. -->
gnored in client component templates. -->
<component is="script">window.users = []</component> <component is="script">window.users = []</component>
<component is="script" src="user-1.js"></component> <component is="script" src="user-1.js"></component>
<component is="script" src="user-2.js"></component> <component is="script" src="user-2.js"></component>
@@ -149,7 +148,31 @@ gnored in client component templates. -->
}, },
template: ` template: `
<metainfo> <metainfo>
<template v-slot:body><span/></template> <template v-slot:base="{ content, metainfo }">http://nuxt.dev:3000{{ content }}</template>
<template v-slot:title="{ content, metainfo }">{{ content }} - {{ metainfo.description }} - Hello</template>
<template v-slot:og(title)="{ content, metainfo, og }">
{{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again
</template>
<!-- // TODO: Using script triggers [Vue warn]: Template compilation error: Tags with side effect (<script> and <style>) are ignored in client component templates. -->
<component is="script">window.users = []</component>
<component is="script" src="user-1.js"></component>
<component is="script" src="user-2.js"></component>
<template v-slot:head="{ metainfo }">
<!--[if IE]>
// -> Reactivity is not supported by Vue in comments, all comments are ignored
<component is="script" :src="metainfo.script[0].src" ></component>
// -> but a static file should work
<script src="user-3.js" ></script>
// -> altho Vue probably strips comments in production builds (but can be configged afaik)
<![endif]-->
<component is="script" :src="metainfo.script[0].src" ></component>
</template>
<template v-slot:body>
<component is="script" src="user-4.js"></component>
</template>
</metainfo> </metainfo>
<div id="app"> <div id="app">
+5 -7
View File
@@ -1,10 +1,6 @@
import { defineComponent, VNodeProps } from 'vue' import { defineComponent } from 'vue'
import type { VNodeProps, AllowedComponentProps, ComponentCustomProps } from 'vue'
import { getCurrentManager } from './useApi' import { getCurrentManager } from './useApi'
import { MetainfoActive } from './types'
export interface MetainfoProps {
metainfo: MetainfoActive
}
export const MetainfoImpl = defineComponent({ export const MetainfoImpl = defineComponent({
name: 'Metainfo', name: 'Metainfo',
@@ -23,6 +19,8 @@ export const MetainfoImpl = defineComponent({
export const Metainfo = (MetainfoImpl as any) as { export const Metainfo = (MetainfoImpl as any) as {
new (): { new (): {
$props: VNodeProps & MetainfoProps $props: AllowedComponentProps &
ComponentCustomProps &
VNodeProps
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
import { Config } from '../types' import { MetaConfig } from '../types'
export const defaultConfig: Config = { export const defaultConfig: MetaConfig = {
body: { body: {
tag: 'script', tag: 'script',
to: 'body' to: 'body'
+8 -26
View File
@@ -1,32 +1,14 @@
import { isArray } from '@vue/shared' import type { MetaTagConfigKey, MetaTagName } from '../types'
import { Config } from '../types'
import { tags } from './tags' import { tags } from './tags'
export function hasConfig (name: string, config: Config): boolean { export function getTagConfigItem (
return !!config[name] || !!tags[name] tagOrName: Array<MetaTagName>,
} key: MetaTagConfigKey
export function getConfigByKey (
tagOrName: string | Array<string>,
key: string,
config: Config
): any { ): any {
if (config && key in config) { for (const name of tagOrName) {
return config[key] const tag = tags[name]
} if (name && tag) {
return tag[key]
if (isArray(tagOrName)) {
for (const name of tagOrName) {
if (name && name in tags) {
return tags[name][key]
}
} }
return
}
if (tagOrName in tags) {
const tag = tags[tagOrName]
return tag[key]
} }
} }
+3 -13
View File
@@ -1,17 +1,9 @@
import type { MetaTagsConfig } from '../types'
/* /*
* This is a fixed config for real HTML tags * This is a fixed config for real HTML tags
*
* TODO: we probably dont need all attributes
*/ */
export const tags: MetaTagsConfig = {
export interface TagConfig {
keyAttribute?: string
contentAsAttribute?: boolean | string
attributes: boolean | Array<string>
[key: string]: any
}
const tags: { [key: string]: TagConfig } = {
title: { title: {
attributes: false attributes: false
}, },
@@ -61,5 +53,3 @@ const tags: { [key: string]: TagConfig } = {
attributes: false attributes: false
} }
} }
export { tags }
+68 -41
View File
@@ -1,46 +1,54 @@
import { h, reactive, onUnmounted, Teleport, VNode, Comment } from 'vue' import { h, reactive, onUnmounted, Teleport, Comment, getCurrentInstance } from 'vue'
import type { VNode } from 'vue'
import { isArray, isFunction } from '@vue/shared' import { isArray, isFunction } from '@vue/shared'
import { createMergedObject } from './object-merge' import { createMergedObject } from './object-merge'
import { renderMeta } from './render' import { renderMeta } from './render'
import { metaInfoKey } from './symbols' import { metaActiveKey } from './symbols'
import { Metainfo } from './Metainfo' import { Metainfo } from './Metainfo'
import type { ResolveMethod } from './object-merge' import type { ResolveMethod } from './object-merge'
import type { Manager, Config, Resolver, MetaContext, MetainfoActive } from './types' import type {
MetaActive,
MetaConfig,
MetaManager,
MetaResolver,
MetaResolveContext,
MetaTeleports
} from './types'
export const ssrAttribute = 'data-vm-ssr' export const ssrAttribute = 'data-vm-ssr'
export const active: MetainfoActive = reactive({}) export const active: MetaActive = reactive({})
export function addVnode (teleports: any, to: string, _vnodes: VNode | Array<VNode>) { export function addVnode (teleports: MetaTeleports, to: string, vnodes: VNode | Array<VNode>): void {
const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]) as Array<VNode> const nodes = (isArray(vnodes) ? vnodes : [vnodes]) as Array<VNode>
if (!__BROWSER__) { if (__BROWSER__) {
// dont add ssrAttribute for attribute vnode placeholder
if (!to.endsWith('Attrs')) {
vnodes.forEach((vnode) => {
if (!vnode.props) {
vnode.props = {}
}
vnode.props[ssrAttribute] = true
})
}
} else {
// 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
vnodes.forEach((vnode, idx) => { nodes.forEach((vnode, idx) => {
if (vnode.type === Comment) { if (vnode.type === Comment) {
vnodes.splice(idx, 1) nodes.splice(idx, 1)
} }
}) })
// 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(...vnodes) teleports[to].push(...nodes)
} }
export function createMetaManager (config: Config, resolver: Resolver | ResolveMethod): Manager { export function createMetaManager (config: MetaConfig, resolver: MetaResolver | ResolveMethod): MetaManager {
let cleanedUpSsr = false
const resolve: ResolveMethod = (options, contexts, active, key, pathSegments) => { const resolve: ResolveMethod = (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)
@@ -51,21 +59,23 @@ export function createMetaManager (config: Config, resolver: Resolver | ResolveM
const { addSource, delSource } = createMergedObject(resolve, active) const { addSource, delSource } = createMergedObject(resolve, active)
let cleanedUpSsr = false
// TODO: validate resolver // TODO: validate resolver
const manager: Manager = { const manager: MetaManager = {
config, config,
install (app) { install (app) {
app.component('Metainfo', Metainfo) app.component('Metainfo', Metainfo)
app.config.globalProperties.$metaManager = manager app.config.globalProperties.$metaManager = manager
app.provide(metaInfoKey, active) app.provide(metaActiveKey, active)
}, },
addMeta (metaObj, vm) { addMeta (metaObj, vm) {
const resolveContext: MetaContext = { vm } if (!vm) {
vm = getCurrentInstance() || undefined
}
const resolveContext: MetaResolveContext = { vm }
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
resolver.setup(resolveContext) resolver.setup(resolveContext)
} }
@@ -84,14 +94,14 @@ export function createMetaManager (config: Config, resolver: Resolver | ResolveM
} }
}, },
render ({ slots }: any = {}): Array<VNode> { render ({ slots } = {}) {
// cleanup ssr tags if not yet done // cleanup ssr tags if not yet done
if (__BROWSER__ && !cleanedUpSsr) { if (__BROWSER__ && !cleanedUpSsr) {
cleanedUpSsr = true cleanedUpSsr = true
// Listen for DOM loaded because tags in the body couldnt be loaded // Listen for DOM loaded because tags in the body couldnt
// yet once the manager does it first render // have loaded yet once the manager does it first render
// (preferable there should only be one render on hydration) // (preferable there should only be one meta render on hydration)
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`) const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`)
@@ -101,36 +111,53 @@ export function createMetaManager (config: Config, resolver: Resolver | ResolveM
}) })
} }
const teleports: { [key: string]: VNode | Array<VNode> } = {} const teleports: MetaTeleports = {}
for (const key in active) { for (const key in active) {
const config = this.config[key] || {} const config = this.config[key] || {}
const vnode = renderMeta( let renderedNodes = renderMeta(
{ metainfo: active, slots }, { metainfo: active, slots },
key, key,
active[key], active[key],
config config
) )
if (!vnode) { if (!renderedNodes) {
continue continue
} }
const vnodes = isArray(vnode) ? vnode : [vnode] if (!isArray(renderedNodes)) {
renderedNodes = [renderedNodes]
}
const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head') let defaultTo = key !== 'base' && active[key].to
for (const { to, vnode } of vnodes) { if (!defaultTo && 'to' in config) {
addVnode(teleports, to || defaultTo, vnode) defaultTo = config.to
}
if (!defaultTo && 'attributesFor' in config) {
defaultTo = key
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode)
} }
} }
if (slots) { if (slots) {
for (const tag in slots) { for (const slotName in slots) {
const slotFn = slots[tag] const tagName = slotName === 'default' ? 'head' : slotName
if (isFunction(slotFn)) {
addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })) // 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 }))
} }
} }
} }
+30 -30
View File
@@ -31,7 +31,14 @@ export type MergeContext = {
sources: Array<MergeSource> sources: Array<MergeSource>
} }
export const createMergedObject = (resolve: ResolveMethod, active: MergedObject = {}) => { export type MergedObjectBuilder = {
context: MergeContext
compute: () => void
addSource: (source: MergeSource, resolveContext: ResolveContext | undefined, recompute?: Boolean) => any
delSource: (sourceOrProxy: MergeSource, recompute?: boolean) => boolean
}
export const createMergedObject = (resolve: ResolveMethod, active: MergedObject = {}): MergedObjectBuilder => {
const sources: Array<MergeSource> = [] const sources: Array<MergeSource> = []
if (!active) { if (!active) {
@@ -44,41 +51,34 @@ export const createMergedObject = (resolve: ResolveMethod, active: MergedObject
sources sources
} }
const compute = () => recompute(context) const compute: () => void = () => recompute(context)
const addSource = (source: MergeSource, resolveContext: ResolveContext | undefined, recompute: Boolean = false) => { return {
const proxy = createProxy(context, source, resolveContext || {}) context,
compute,
if (recompute) { addSource: (source, resolveContext, recompute = false) => {
compute() const proxy = createProxy(context, source, resolveContext || {})
}
return proxy
}
const delSource = (sourceOrProxy: MergeSource, recompute: boolean = true): boolean => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy)
if (index > -1) {
sources.splice(index, 1)
if (recompute) { if (recompute) {
compute() compute()
} }
return true return proxy
},
delSource: (sourceOrProxy, recompute = true) => {
const index = sources.findIndex(src => src === sourceOrProxy || src[PROXY_TARGET] === sourceOrProxy)
if (index > -1) {
sources.splice(index, 1)
if (recompute) {
compute()
}
return true
}
return false
} }
return false
}
return {
context,
active,
resolve,
sources,
addSource,
delSource,
compute
} }
} }
+66 -76
View File
@@ -1,7 +1,19 @@
import { h, VNode } from 'vue' import { h } from 'vue'
import { isArray, isString } from '@vue/shared' import { isArray, isString } from '@vue/shared'
import { getConfigByKey } from './config' import { getTagConfigItem } from './config'
import { TODO } from './types' import type {
MetaConfigSectionAttribute,
MetaConfigSectionGroup,
MetaConfigSectionTag,
MetaConfigSection,
MetaGroupConfig,
MetaRenderContext,
MetaRenderedNode,
MetaRendered,
MetaTagConfigKey,
SlotScopeProperties,
TODO
} from './types'
const cachedElements: { const cachedElements: {
[key: string]: { [key: string]: {
@@ -10,63 +22,37 @@ const cachedElements: {
} }
} = {} } = {}
export interface RenderContext {
slots: any
[key: string]: TODO
}
export interface GroupConfig {
group: string
data: Array<TODO> | TODO
tagNamespace?: string
fullName?: string
slotName?: string
}
export interface SlotScopeProperties {
content: any
metainfo: any
[key: string]: any
}
export type RenderedMetainfoNode = {
vnode: VNode
to?: string
}
export type RenderedMetainfo = Array<RenderedMetainfoNode>
export function renderMeta ( export function renderMeta (
context: RenderContext, context: MetaRenderContext,
key: string, key: string,
data: TODO, data: TODO,
config: TODO config: MetaConfigSection
): void | RenderedMetainfo | RenderedMetainfoNode { ): void | MetaRendered | MetaRenderedNode {
// console.info('renderMeta', key, data, config) // console.info('renderMeta', key, data, config)
if (config.attributesFor) { if ('attributesFor' in config) {
return renderAttributes(context, key, data, config) return renderAttributes(context, key, data, config as MetaConfigSectionAttribute)
} }
if (config.group) { if ('group' in config) {
return renderGroup(context, key, data, config) return renderGroup(context, key, data, config as MetaConfigSectionGroup)
} }
return renderTag(context, key, data, config) return renderTag(context, key, data, config)
} }
export function renderGroup ( export function renderGroup (
context: RenderContext, context: MetaRenderContext,
key: string, key: string,
data: TODO, data: TODO,
config: TODO config: MetaConfigSectionGroup
): RenderedMetainfo | RenderedMetainfoNode { ): MetaRendered | MetaRenderedNode {
// console.info('renderGroup', key, data, config) // console.info('renderGroup', key, data, config)
if (isArray(data)) { if (isArray(data)) {
if (__DEV__) { if (__DEV__) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Specifying an array for group properties isnt supported mostly as we didnt found a use-case for this yet. If you have one, please create an issue on the vue-meta repo') console.warn('Specifying an array for group properties isnt supported')
} }
// config.attributes = getConfigKey([key, config.tag], 'attributes', config) // config.attributes = getConfigKey([key, config.tag], 'attributes', config)
return [] return []
@@ -74,7 +60,7 @@ export function renderGroup (
return Object.keys(data) return Object.keys(data)
.map((childKey) => { .map((childKey) => {
const groupConfig: GroupConfig = { const groupConfig: MetaGroupConfig = {
group: key, group: key,
data data
} }
@@ -89,22 +75,22 @@ export function renderGroup (
groupConfig.slotName = `${namespace}(${childKey})` groupConfig.slotName = `${namespace}(${childKey})`
} }
return renderTag(context, key, data[childKey], config, groupConfig) return renderTag(context, key, data[childKey], config as MetaConfigSectionTag, groupConfig)
}) })
.flat() .flat()
} }
export function renderTag ( export function renderTag (
context: RenderContext, context: MetaRenderContext,
key: string, key: string,
data: TODO, data: TODO,
config: TODO = {}, config: MetaConfigSectionTag = {},
groupConfig?: GroupConfig groupConfig?: MetaGroupConfig
): RenderedMetainfo | RenderedMetainfoNode { ): MetaRendered | MetaRenderedNode {
// console.info('renderTag', key, data, config, groupConfig) // console.info('renderTag', key, data, config, groupConfig)
const contentAttributes = ['content', 'json', 'rawContent'] const contentAttributes = ['content', 'json', 'rawContent']
const getConfig = (key: string) => getConfigByKey([tag, config.tag], key, config) const getTagConfig = (key: MetaTagConfigKey) => getTagConfigItem([tag, config.tag], key)
if (isArray(data)) { if (isArray(data)) {
return data return data
@@ -116,7 +102,7 @@ export function renderTag (
const { tag = config.tag || key } = data const { tag = config.tag || key } = data
let content let content: string = ''
let hasChilds: boolean = false let hasChilds: boolean = false
let isRaw: boolean = false let isRaw: boolean = false
@@ -174,19 +160,19 @@ export function renderTag (
if (hasChilds) { if (hasChilds) {
content = getSlotContent(context, slotName, content, data) content = getSlotContent(context, slotName, content, data)
} else { } else {
const contentAsAttribute = getConfig('contentAsAttribute') const contentAsAttribute = !!getTagConfig('contentAsAttribute')
let valueAttribute = config.valueAttribute let { valueAttribute } = config
if (!valueAttribute && contentAsAttribute) { if (!valueAttribute && contentAsAttribute) {
const tagAttributes = getConfig('attributes') const [tagAttribute] = getTagConfig('attributes')
valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0] valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute
} }
if (!valueAttribute) { if (!valueAttribute) {
content = getSlotContent(context, slotName, content, data) content = getSlotContent(context, slotName, content, data)
} else { } else {
if (!config.nameless) { const { nameless, keyAttribute } = config
const keyAttribute = getConfig('keyAttribute') if (!nameless) {
if (keyAttribute) { if (keyAttribute) {
attributes[keyAttribute] = fullName attributes[keyAttribute] = fullName
} }
@@ -199,7 +185,7 @@ export function renderTag (
groupConfig groupConfig
) )
content = undefined content = ''
} }
} }
@@ -213,15 +199,13 @@ export function renderTag (
// console.log(' CONTENT', content) // console.log(' CONTENT', content)
// // console.log(data, attributes, config) // // console.log(data, attributes, config)
let vnode if (isRaw && content) {
if (isRaw) {
attributes.innerHTML = content attributes.innerHTML = content
vnode = h(finalTag, attributes)
} else {
vnode = h(finalTag, attributes, content)
} }
// Ignore empty string content
const vnode = h(finalTag, attributes, content || undefined)
return { return {
to: data.to, to: data.to,
vnode vnode
@@ -229,14 +213,17 @@ export function renderTag (
} }
export function renderAttributes ( export function renderAttributes (
context: RenderContext, context: MetaRenderContext,
key: string, key: string,
data: TODO, data: TODO,
config: TODO = {} config: MetaConfigSectionAttribute
): RenderedMetainfoNode | void { ): MetaRenderedNode | void {
// console.info('renderAttributes', key, data, config) // console.info('renderAttributes', key, data, config)
const { attributesFor } = config const { attributesFor } = config
if (!attributesFor) {
return
}
if (!__BROWSER__) { if (!__BROWSER__) {
// render attributes in a placeholder vnode so Vue // render attributes in a placeholder vnode so Vue
@@ -252,13 +239,13 @@ export function renderAttributes (
if (__DEV__ && !el) { if (__DEV__ && !el) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('Could not find element with selector', attributesFor, ', won\'t render attributes') console.error('Could not find element for selector', attributesFor, ', won\'t render attributes')
return return
} }
if (__DEV__ && el2) { if (__DEV__ && el2) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn('Found multiple elements with selector', attributesFor) console.warn('Found multiple elements for selector', attributesFor)
} }
cachedElements[attributesFor] = { cachedElements[attributesFor] = {
@@ -272,7 +259,7 @@ export function renderAttributes (
for (const attr in data) { for (const attr in data) {
const content = getSlotContent(context, `${key}(${attr})`, data[attr], data) const content = getSlotContent(context, `${key}(${attr})`, data[attr], data)
el.setAttribute(attr, `${content || ''}`) el.setAttribute(attr, content || '')
if (!attrs.includes(attr)) { if (!attrs.includes(attr)) {
attrs.push(attr) attrs.push(attr)
@@ -286,28 +273,31 @@ export function renderAttributes (
} }
export function getSlotContent ( export function getSlotContent (
{ metainfo, slots }: RenderContext, { metainfo, slots }: MetaRenderContext,
slotName: string, slotName: string,
content: any, content: string,
groupConfig?: GroupConfig groupConfig?: MetaGroupConfig
): TODO { ): string {
if (!slots || !slots[slotName]) { const slot = slots && slots[slotName]
if (!slot) {
return content return content
} }
const slotProps: SlotScopeProperties = { const slotScopeProps: SlotScopeProperties = {
content, content,
metainfo metainfo
} }
if (groupConfig && groupConfig.group) { if (groupConfig && groupConfig.group) {
slotProps[groupConfig.group] = groupConfig.data const { group, data } = groupConfig
slotScopeProps[group] = data
} }
const slotContent = slots[slotName](slotProps) const slotContent = slot(slotScopeProps)
if (slotContent && slotContent.length) { if (slotContent && slotContent.length) {
return slotContent[0].children const { children } = slotContent[0]
return children ? children.toString() : ''
} }
return content return content
+4 -4
View File
@@ -1,12 +1,12 @@
import { ResolveContext, ResolveMethod } from '../object-merge' import { ResolveContext, ResolveMethod } from '../object-merge'
import { MetaContext } from '../types' import { MetaResolveContext } from '../types'
import { resolveOption } from '.' import { resolveOption } from '.'
type MergeContextDeepest = MetaContext & { type MergeResolveContextDeepest = MetaResolveContext & {
depth: number depth: number
} }
export function setup (context: MergeContextDeepest): void { export function setup (context: MergeResolveContextDeepest): void {
let depth: number = 0 let depth: number = 0
if (context.vm) { if (context.vm) {
@@ -25,7 +25,7 @@ export function setup (context: MergeContextDeepest): void {
} }
export const resolve: ResolveMethod = resolveOption((acc: any, context: ResolveContext) => { export const resolve: ResolveMethod = resolveOption((acc: any, context: ResolveContext) => {
const { depth } = (context as unknown as MergeContextDeepest) const { depth } = (context as unknown as MergeResolveContextDeepest)
if (!acc || depth > acc) { if (!acc || depth > acc) {
return acc return acc
} }
+1 -1
View File
@@ -1,7 +1,7 @@
import type { App } from 'vue' import type { App } from 'vue'
import type { SSRContext } from '@vue/server-renderer' import type { SSRContext } from '@vue/server-renderer'
// rollup doesnt like an import, cant find export so use require // rollup doesnt like an import as it cant find the export so use require
const { renderToString } = require('@vue/server-renderer') const { renderToString } = require('@vue/server-renderer')
export async function renderToStringWithMeta (app: App): Promise<[string, SSRContext]> { export async function renderToStringWithMeta (app: App): Promise<[string, SSRContext]> {
+5 -6
View File
@@ -1,8 +1,7 @@
import { InjectionKey } from 'vue' import { InjectionKey } from 'vue'
import { MetainfoActive } from './types' import { MetaActive } from './types'
export const hasSymbol = export const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'
typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'
export const PolySymbol = (name: string) => export const PolySymbol = (name: string) =>
// vm = vue meta // vm = vue meta
@@ -10,6 +9,6 @@ export const PolySymbol = (name: string) =>
? Symbol(__DEV__ ? '[vue-meta]: ' + name : name) ? Symbol(__DEV__ ? '[vue-meta]: ' + name : name)
: (__DEV__ ? '[vue-meta]: ' : '_vm_') + name : (__DEV__ ? '[vue-meta]: ' : '_vm_') + name
export const metaInfoKey = PolySymbol( export const metaActiveKey = /*#__PURE__*/ PolySymbol(
__DEV__ ? 'metainfo' : 'mi' __DEV__ ? 'active_meta' : 'am'
) as InjectionKey<MetainfoActive> ) as InjectionKey<MetaActive>
+40
View File
@@ -0,0 +1,40 @@
export type MetaConfigSectionKey = 'tag' | 'to' | 'keyAttribute' | 'valueAttribute' | 'nameless' | 'group' | 'namespaced' | 'namespacedAttribute' | 'attributesFor'
export interface MetaConfigSectionTag {
tag?: string
to?: string
keyAttribute?: string
valueAttribute?: string
nameless?: boolean
}
export type MetaConfigSectionGroup = {
group: boolean
namespaced?: boolean
namespacedAttribute?: boolean
}
export type MetaConfigSectionAttribute = {
attributesFor: string
}
export type MetaConfigSection = MetaConfigSectionGroup | MetaConfigSectionTag | MetaConfigSectionAttribute
export interface MetaConfig {
[key: string]: MetaConfigSection
}
export type MetaTagConfigKey = 'keyAttribute' | 'contentAsAttribute' | 'attributes'
export interface MetaTagConfig {
keyAttribute?: string
contentAsAttribute?: boolean | string
attributes: boolean | Array<string>
}
export type MetaTagName = 'title' | 'base' | 'meta' | 'link' | 'style' | 'script' | 'noscript'
export type MetaTagsConfig = {
[key in MetaTagName]: MetaTagConfig
}
+91 -49
View File
@@ -1,72 +1,114 @@
import type { App, VNode, ComponentInternalInstance } from 'vue' import type { App, VNode, Slots, ComponentInternalInstance } from 'vue'
import type { MergedObject, ResolveContext, ResolveMethod } from '../object-merge' import type { MergedObject, ResolveContext, ResolveMethod } from '../object-merge'
import type { MetaConfig } from './config'
export * from './config'
export type TODO = any export type TODO = any
export type MetainfoInput = { /**
* Proxied meta source for tracking changes and updating the active meta daa
*/
export interface MetaSourceProxy extends MergedObject {}
/**
* Metainfo data source input by the user through the useMeta fn
*/
export type MetaSource = {
[key: string]: TODO [key: string]: TODO
} }
export type MetaContext = ResolveContext & { /**
* Return value of the useMeta api
*/
export type MetaProxy = {
meta: MetaSourceProxy
unmount: Function | false
}
/**
* The active/aggregated meta data currently rendered
*/
export interface MetaActive {
[key: string]: TODO
}
/**
* Context passed to the meta resolvers
*/
export type MetaResolveContext = ResolveContext & {
vm: ComponentInternalInstance | undefined vm: ComponentInternalInstance | undefined
} }
export interface ConfigOption { export type MetaResolveSetup = (context: MetaResolveContext) => void
tag?: string
to?: string
group?: boolean
keyAttribute?: string
valueAttribute?: string
nameless?: boolean
namespaced?: boolean
namespacedAttribute?: boolean
attributesFor?: string
}
export interface Config { export type MetaResolver = {
[key: string]: ConfigOption setup?: MetaResolveSetup
}
export interface MetainfoProxy extends MergedObject {
}
export interface MetainfoActive {
[key: string]: TODO
}
export type MetaProxy = {
meta: MetainfoProxy
unmount: TODO
}
export type ResolveSetup = (context: MetaContext) => void
export type Resolver = {
setup?: ResolveSetup
resolve: ResolveMethod resolve: ResolveMethod
} }
export type Manager = { /**
readonly config: Config * The meta manager
*/
export type MetaManager = {
readonly config: MetaConfig
install(app: App): void install(app: App): void
addMeta(obj: MetainfoInput, vm?: ComponentInternalInstance): MetaProxy addMeta(source: MetaSource, vm?: ComponentInternalInstance): MetaProxy
render(ctx: { slots?: any }): Array<VNode> render(ctx?: { slots?: Slots }): Array<VNode>
} }
/**
* @internal
*/
export type MetaTeleports = {
[key: string]: Array<VNode>
}
/**
* @internal
*/
export interface MetaRenderContext {
slots?: Slots
metainfo: MetaActive
}
/**
* @internal
*/
export interface MetaGroupConfig {
group: string
data: Array<TODO> | TODO
tagNamespace?: string
fullName?: string
slotName?: string
}
/**
* @internal
*/
export interface SlotScopeProperties {
content: string
metainfo: MetaActive
[key: string]: any
}
/**
* @internal
*/
export type MetaRenderedNode = {
vnode: VNode
to?: string
}
/**
* @internal
*/
export type MetaRendered = Array<MetaRenderedNode>
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface ComponentInternalInstance { interface ComponentInternalInstance {
$metaManager: Manager $metaManager: MetaManager
}
}
declare global {
namespace NodeJS {
interface Process {
client: boolean
server: boolean
}
} }
} }
+12 -8
View File
@@ -1,16 +1,20 @@
import { inject, getCurrentInstance, ComponentInternalInstance } from 'vue' import { inject, getCurrentInstance, ComponentInternalInstance } from 'vue'
import { metaInfoKey } from './symbols' import { metaActiveKey } from './symbols'
import type { Manager, MetainfoActive, MetainfoInput, MetaProxy } from './types' import type { MetaManager, MetaActive, MetaSource, MetaProxy } from './types'
export function getCurrentManager (vm?: ComponentInternalInstance): Manager { export function getCurrentManager (vm?: ComponentInternalInstance): MetaManager | undefined {
if (!vm) { if (!vm) {
vm = getCurrentInstance()! vm = getCurrentInstance() || undefined
}
if (!vm) {
return undefined
} }
return vm.appContext.config.globalProperties.$metaManager return vm.appContext.config.globalProperties.$metaManager
} }
export function useMeta (obj: MetainfoInput, manager?: Manager): MetaProxy { export function useMeta (source: MetaSource, manager?: MetaManager): MetaProxy {
const vm = getCurrentInstance() const vm = getCurrentInstance()
if (!manager && vm) { if (!manager && vm) {
@@ -22,9 +26,9 @@ export function useMeta (obj: MetainfoInput, manager?: Manager): MetaProxy {
throw new Error('No manager or current instance') throw new Error('No manager or current instance')
} }
return manager.addMeta(obj, vm || undefined) return manager.addMeta(source, vm || undefined)
} }
export function useMetainfo (): MetainfoActive { export function useActiveMeta (): MetaActive {
return inject(metaInfoKey)! return inject(metaActiveKey)!
} }