From 45704e0a3187b168bee43e153aedfb82f3fe1d4c Mon Sep 17 00:00:00 2001 From: pimlie Date: Mon, 1 Feb 2021 00:08:28 +0100 Subject: [PATCH] fix: consolidate types --- .eslintrc | 7 +- .versionrc | 2 +- dist/src/Metainfo.d.ts | 12 -- dist/src/Metainfo.d.ts.map | 1 - dist/src/config/default.d.ts | 3 - dist/src/config/default.d.ts.map | 1 - dist/src/config/helpers.d.ts | 4 - dist/src/config/helpers.d.ts.map | 1 - dist/src/config/index.d.ts | 4 - dist/src/config/index.d.ts.map | 1 - dist/src/config/tags.d.ts | 11 -- dist/src/config/tags.d.ts.map | 1 - dist/src/index.d.ts | 9 - dist/src/index.d.ts.map | 1 - dist/src/manager.d.ts | 8 - dist/src/manager.d.ts.map | 1 - dist/src/object-merge/constants.d.ts | 5 - dist/src/object-merge/constants.d.ts.map | 1 - dist/src/object-merge/index.d.ts | 25 --- dist/src/object-merge/index.d.ts.map | 1 - dist/src/object-merge/proxy.d.ts | 4 - dist/src/object-merge/proxy.d.ts.map | 1 - dist/src/object-merge/recompute.d.ts | 4 - dist/src/object-merge/recompute.d.ts.map | 1 - dist/src/render.d.ts | 29 --- dist/src/render.d.ts.map | 1 - dist/src/resolvers/deepest.d.ts | 9 - dist/src/resolvers/deepest.d.ts.map | 1 - dist/src/resolvers/index.d.ts | 4 - dist/src/resolvers/index.d.ts.map | 1 - dist/src/ssr.d.ts | 4 - dist/src/ssr.d.ts.map | 1 - dist/src/symbols.d.ts | 6 - dist/src/symbols.d.ts.map | 1 - dist/src/types/index.d.ts | 59 ------ dist/src/types/index.d.ts.map | 1 - dist/src/useApi.d.ts | 6 - dist/src/useApi.d.ts.map | 1 - dist/src/utils/clone.d.ts | 2 - dist/src/utils/clone.d.ts.map | 1 - dist/src/utils/collection.d.ts | 2 - dist/src/utils/collection.d.ts.map | 1 - dist/src/utils/debug.d.ts | 13 -- dist/src/utils/debug.d.ts.map | 1 - dist/src/utils/index.d.ts | 4 - dist/src/utils/index.d.ts.map | 1 - dist/vue-meta.cjs.js | 229 ++++++++++++----------- dist/vue-meta.cjs.prod.js | 227 +++++++++++----------- dist/vue-meta.d.ts | 166 ++++++++++++++++ dist/vue-meta.esm-browser.js | 229 ++++++++++++----------- dist/vue-meta.esm-browser.min.js | 2 +- dist/vue-meta.esm-bundler.js | 229 ++++++++++++----------- dist/vue-meta.global.js | 229 ++++++++++++----------- dist/vue-meta.global.min.js | 2 +- examples/vue-router/App.js | 33 +++- src/Metainfo.ts | 12 +- src/config/default.ts | 4 +- src/config/helpers.ts | 34 +--- src/config/tags.ts | 16 +- src/manager.ts | 109 +++++++---- src/object-merge/index.ts | 60 +++--- src/render.ts | 142 +++++++------- src/resolvers/deepest.ts | 8 +- src/ssr.ts | 2 +- src/symbols.ts | 11 +- src/types/config.ts | 40 ++++ src/types/index.ts | 140 +++++++++----- src/useApi.ts | 20 +- 68 files changed, 1120 insertions(+), 1082 deletions(-) delete mode 100644 dist/src/Metainfo.d.ts delete mode 100644 dist/src/Metainfo.d.ts.map delete mode 100644 dist/src/config/default.d.ts delete mode 100644 dist/src/config/default.d.ts.map delete mode 100644 dist/src/config/helpers.d.ts delete mode 100644 dist/src/config/helpers.d.ts.map delete mode 100644 dist/src/config/index.d.ts delete mode 100644 dist/src/config/index.d.ts.map delete mode 100644 dist/src/config/tags.d.ts delete mode 100644 dist/src/config/tags.d.ts.map delete mode 100644 dist/src/index.d.ts delete mode 100644 dist/src/index.d.ts.map delete mode 100644 dist/src/manager.d.ts delete mode 100644 dist/src/manager.d.ts.map delete mode 100644 dist/src/object-merge/constants.d.ts delete mode 100644 dist/src/object-merge/constants.d.ts.map delete mode 100644 dist/src/object-merge/index.d.ts delete mode 100644 dist/src/object-merge/index.d.ts.map delete mode 100644 dist/src/object-merge/proxy.d.ts delete mode 100644 dist/src/object-merge/proxy.d.ts.map delete mode 100644 dist/src/object-merge/recompute.d.ts delete mode 100644 dist/src/object-merge/recompute.d.ts.map delete mode 100644 dist/src/render.d.ts delete mode 100644 dist/src/render.d.ts.map delete mode 100644 dist/src/resolvers/deepest.d.ts delete mode 100644 dist/src/resolvers/deepest.d.ts.map delete mode 100644 dist/src/resolvers/index.d.ts delete mode 100644 dist/src/resolvers/index.d.ts.map delete mode 100644 dist/src/ssr.d.ts delete mode 100644 dist/src/ssr.d.ts.map delete mode 100644 dist/src/symbols.d.ts delete mode 100644 dist/src/symbols.d.ts.map delete mode 100644 dist/src/types/index.d.ts delete mode 100644 dist/src/types/index.d.ts.map delete mode 100644 dist/src/useApi.d.ts delete mode 100644 dist/src/useApi.d.ts.map delete mode 100644 dist/src/utils/clone.d.ts delete mode 100644 dist/src/utils/clone.d.ts.map delete mode 100644 dist/src/utils/collection.d.ts delete mode 100644 dist/src/utils/collection.d.ts.map delete mode 100644 dist/src/utils/debug.d.ts delete mode 100644 dist/src/utils/debug.d.ts.map delete mode 100644 dist/src/utils/index.d.ts delete mode 100644 dist/src/utils/index.d.ts.map create mode 100644 dist/vue-meta.d.ts create mode 100644 src/types/config.ts diff --git a/.eslintrc b/.eslintrc index f44d9c9..0a23296 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,7 +4,10 @@ ], "globals": { "__DEV__": true, - "__BROWSER__": false, + "__BROWSER__": false + }, + "rules": { + "spaced-comment": ["error", "always", { "exceptions": ["#__PURE__"] }] }, "overrides": [ { @@ -12,7 +15,7 @@ "examples/**" ], "rules": { - "no-console": "off", + "no-console": "off" } } ] diff --git a/.versionrc b/.versionrc index 6b23bf5..80fdf05 100644 --- a/.versionrc +++ b/.versionrc @@ -2,6 +2,6 @@ "commit-all": true, "scripts": { "postbump": "yarn build", - "precommit": "git add -f dist/*" + "precommit": "git add -f dist/*.js dist/*.d.ts" } } diff --git a/dist/src/Metainfo.d.ts b/dist/src/Metainfo.d.ts deleted file mode 100644 index 3c30815..0000000 --- a/dist/src/Metainfo.d.ts +++ /dev/null @@ -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[] | 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 \ No newline at end of file diff --git a/dist/src/Metainfo.d.ts.map b/dist/src/Metainfo.d.ts.map deleted file mode 100644 index 2476e43..0000000 --- a/dist/src/Metainfo.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/config/default.d.ts b/dist/src/config/default.d.ts deleted file mode 100644 index 755b4e2..0000000 --- a/dist/src/config/default.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Config } from '../types'; -export declare const defaultConfig: Config; -//# sourceMappingURL=default.d.ts.map \ No newline at end of file diff --git a/dist/src/config/default.d.ts.map b/dist/src/config/default.d.ts.map deleted file mode 100644 index 9ae2c7b..0000000 --- a/dist/src/config/default.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/config/helpers.d.ts b/dist/src/config/helpers.d.ts deleted file mode 100644 index b1266a1..0000000 --- a/dist/src/config/helpers.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Config } from '../types'; -export declare function hasConfig(name: string, config: Config): boolean; -export declare function getConfigByKey(tagOrName: string | Array, key: string, config: Config): any; -//# sourceMappingURL=helpers.d.ts.map \ No newline at end of file diff --git a/dist/src/config/helpers.d.ts.map b/dist/src/config/helpers.d.ts.map deleted file mode 100644 index a5fd822..0000000 --- a/dist/src/config/helpers.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/config/index.d.ts b/dist/src/config/index.d.ts deleted file mode 100644 index f01bba6..0000000 --- a/dist/src/config/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './default'; -export * from './helpers'; -export * from './tags'; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/src/config/index.d.ts.map b/dist/src/config/index.d.ts.map deleted file mode 100644 index c7c87d9..0000000 --- a/dist/src/config/index.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/config/tags.d.ts b/dist/src/config/tags.d.ts deleted file mode 100644 index 187cff3..0000000 --- a/dist/src/config/tags.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface TagConfig { - keyAttribute?: string; - contentAsAttribute?: boolean | string; - attributes: boolean | Array; - [key: string]: any; -} -declare const tags: { - [key: string]: TagConfig; -}; -export { tags }; -//# sourceMappingURL=tags.d.ts.map \ No newline at end of file diff --git a/dist/src/config/tags.d.ts.map b/dist/src/config/tags.d.ts.map deleted file mode 100644 index c11d5ad..0000000 --- a/dist/src/config/tags.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/index.d.ts b/dist/src/index.d.ts deleted file mode 100644 index 5cc7385..0000000 --- a/dist/src/index.d.ts +++ /dev/null @@ -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 \ No newline at end of file diff --git a/dist/src/index.d.ts.map b/dist/src/index.d.ts.map deleted file mode 100644 index 2d0dfaa..0000000 --- a/dist/src/index.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/manager.d.ts b/dist/src/manager.d.ts deleted file mode 100644 index ff8a6a2..0000000 --- a/dist/src/manager.d.ts +++ /dev/null @@ -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): void; -export declare function createMetaManager(config: Config, resolver: Resolver | ResolveMethod): Manager; -//# sourceMappingURL=manager.d.ts.map \ No newline at end of file diff --git a/dist/src/manager.d.ts.map b/dist/src/manager.d.ts.map deleted file mode 100644 index 27b7d4f..0000000 --- a/dist/src/manager.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/object-merge/constants.d.ts b/dist/src/object-merge/constants.d.ts deleted file mode 100644 index ad39987..0000000 --- a/dist/src/object-merge/constants.d.ts +++ /dev/null @@ -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 \ No newline at end of file diff --git a/dist/src/object-merge/constants.d.ts.map b/dist/src/object-merge/constants.d.ts.map deleted file mode 100644 index 493f449..0000000 --- a/dist/src/object-merge/constants.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/object-merge/index.d.ts b/dist/src/object-merge/index.d.ts deleted file mode 100644 index 7f48982..0000000 --- a/dist/src/object-merge/index.d.ts +++ /dev/null @@ -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; -export declare type ResolveContext = {}; -export declare type ResolveMethod = (options: Array, contexts: Array, active: MergedObjectValue, key: string | number | symbol, pathSegments: PathSegments) => MergedObjectValue; -export declare type MergeContext = { - resolve: ResolveMethod; - active: MergedObject; - sources: Array; -}; -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 \ No newline at end of file diff --git a/dist/src/object-merge/index.d.ts.map b/dist/src/object-merge/index.d.ts.map deleted file mode 100644 index 51b375a..0000000 --- a/dist/src/object-merge/index.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/object-merge/proxy.d.ts b/dist/src/object-merge/proxy.d.ts deleted file mode 100644 index 7bbeae4..0000000 --- a/dist/src/object-merge/proxy.d.ts +++ /dev/null @@ -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; -//# sourceMappingURL=proxy.d.ts.map \ No newline at end of file diff --git a/dist/src/object-merge/proxy.d.ts.map b/dist/src/object-merge/proxy.d.ts.map deleted file mode 100644 index 6c7746b..0000000 --- a/dist/src/object-merge/proxy.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/object-merge/recompute.d.ts b/dist/src/object-merge/recompute.d.ts deleted file mode 100644 index 1356acd..0000000 --- a/dist/src/object-merge/recompute.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { MergeContext, MergeSource, MergedObject, PathSegments } from '.'; -export declare const allKeys: (source?: MergeSource | undefined, ...sources: Array) => Array; -export declare const recompute: (context: MergeContext, sources?: MergeSource[] | undefined, target?: MergedObject | undefined, path?: PathSegments) => void; -//# sourceMappingURL=recompute.d.ts.map \ No newline at end of file diff --git a/dist/src/object-merge/recompute.d.ts.map b/dist/src/object-merge/recompute.d.ts.map deleted file mode 100644 index b6f437b..0000000 --- a/dist/src/object-merge/recompute.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/render.d.ts b/dist/src/render.d.ts deleted file mode 100644 index 8a83e86..0000000 --- a/dist/src/render.d.ts +++ /dev/null @@ -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; - 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; -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 \ No newline at end of file diff --git a/dist/src/render.d.ts.map b/dist/src/render.d.ts.map deleted file mode 100644 index d5bc022..0000000 --- a/dist/src/render.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/resolvers/deepest.d.ts b/dist/src/resolvers/deepest.d.ts deleted file mode 100644 index 072bf06..0000000 --- a/dist/src/resolvers/deepest.d.ts +++ /dev/null @@ -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 \ No newline at end of file diff --git a/dist/src/resolvers/deepest.d.ts.map b/dist/src/resolvers/deepest.d.ts.map deleted file mode 100644 index 0046b24..0000000 --- a/dist/src/resolvers/deepest.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/resolvers/index.d.ts b/dist/src/resolvers/index.d.ts deleted file mode 100644 index dacfffb..0000000 --- a/dist/src/resolvers/index.d.ts +++ /dev/null @@ -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 \ No newline at end of file diff --git a/dist/src/resolvers/index.d.ts.map b/dist/src/resolvers/index.d.ts.map deleted file mode 100644 index 03ef0c0..0000000 --- a/dist/src/resolvers/index.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/ssr.d.ts b/dist/src/ssr.d.ts deleted file mode 100644 index 4eccc4e..0000000 --- a/dist/src/ssr.d.ts +++ /dev/null @@ -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 \ No newline at end of file diff --git a/dist/src/ssr.d.ts.map b/dist/src/ssr.d.ts.map deleted file mode 100644 index c1b5511..0000000 --- a/dist/src/ssr.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/symbols.d.ts b/dist/src/symbols.d.ts deleted file mode 100644 index 4841feb..0000000 --- a/dist/src/symbols.d.ts +++ /dev/null @@ -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; -//# sourceMappingURL=symbols.d.ts.map \ No newline at end of file diff --git a/dist/src/symbols.d.ts.map b/dist/src/symbols.d.ts.map deleted file mode 100644 index ce5ebe5..0000000 --- a/dist/src/symbols.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/types/index.d.ts b/dist/src/types/index.d.ts deleted file mode 100644 index 40853f3..0000000 --- a/dist/src/types/index.d.ts +++ /dev/null @@ -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; -}; -declare module '@vue/runtime-core' { - interface ComponentInternalInstance { - $metaManager: Manager; - } -} -declare global { - namespace NodeJS { - interface Process { - client: boolean; - server: boolean; - } - } -} -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/src/types/index.d.ts.map b/dist/src/types/index.d.ts.map deleted file mode 100644 index aca35cb..0000000 --- a/dist/src/types/index.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/useApi.d.ts b/dist/src/useApi.d.ts deleted file mode 100644 index 518dad5..0000000 --- a/dist/src/useApi.d.ts +++ /dev/null @@ -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 \ No newline at end of file diff --git a/dist/src/useApi.d.ts.map b/dist/src/useApi.d.ts.map deleted file mode 100644 index d56817e..0000000 --- a/dist/src/useApi.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/utils/clone.d.ts b/dist/src/utils/clone.d.ts deleted file mode 100644 index 63a7155..0000000 --- a/dist/src/utils/clone.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare function clone(v: any): any; -//# sourceMappingURL=clone.d.ts.map \ No newline at end of file diff --git a/dist/src/utils/clone.d.ts.map b/dist/src/utils/clone.d.ts.map deleted file mode 100644 index c49ae05..0000000 --- a/dist/src/utils/clone.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/utils/collection.d.ts b/dist/src/utils/collection.d.ts deleted file mode 100644 index dd0241d..0000000 --- a/dist/src/utils/collection.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare const pluck: (collection: Array, key: string, callback?: ((row: any) => void) | undefined) => any[]; -//# sourceMappingURL=collection.d.ts.map \ No newline at end of file diff --git a/dist/src/utils/collection.d.ts.map b/dist/src/utils/collection.d.ts.map deleted file mode 100644 index d5acf45..0000000 --- a/dist/src/utils/collection.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/utils/debug.d.ts b/dist/src/utils/debug.d.ts deleted file mode 100644 index 957d039..0000000 --- a/dist/src/utils/debug.d.ts +++ /dev/null @@ -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 \ No newline at end of file diff --git a/dist/src/utils/debug.d.ts.map b/dist/src/utils/debug.d.ts.map deleted file mode 100644 index 7dd1886..0000000 --- a/dist/src/utils/debug.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/src/utils/index.d.ts b/dist/src/utils/index.d.ts deleted file mode 100644 index e2a8557..0000000 --- a/dist/src/utils/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './clone'; -export * from './collection'; -export * from './debug'; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/src/utils/index.d.ts.map b/dist/src/utils/index.d.ts.map deleted file mode 100644 index ecfa749..0000000 --- a/dist/src/utils/index.d.ts.map +++ /dev/null @@ -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"} \ No newline at end of file diff --git a/dist/vue-meta.cjs.js b/dist/vue-meta.cjs.js index 3fd46f0..1517249 100644 --- a/dist/vue-meta.cjs.js +++ b/dist/vue-meta.cjs.js @@ -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 - * - * TODO: we probably dont need all attributes */ const tags = { title: { @@ -166,24 +145,34 @@ const tags = { } }; -function getConfigByKey(tagOrName, key, config) { - if (config && key in config) { - return config[key]; - } - if (isArray(tagOrName)) { - for (const name of tagOrName) { - if (name && name in tags) { - return tags[name][key]; - } +function getTagConfigItem(tagOrName, key) { + for (const name of tagOrName) { + const tag = tags[name]; + if (name && tag) { + return tag[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 const IS_PROXY = Symbol('kIsProxy'); const PROXY_SOURCES = Symbol('kProxySources'); @@ -443,41 +432,36 @@ const createMergedObject = (resolve, active = {}) => { sources }; const compute = () => recompute(context); - const addSource = (source, resolveContext, recompute = false) => { - const proxy = createProxy(context, source, resolveContext || {}); - if (recompute) { - compute(); - } - 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); + return { + context, + compute, + addSource: (source, resolveContext, recompute = false) => { + const proxy = createProxy(context, source, resolveContext || {}); if (recompute) { 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) { // console.info('renderMeta', key, data, config) - if (config.attributesFor) { + if ('attributesFor' in config) { return renderAttributes(context, key, data, config); } - if (config.group) { + if ('group' in config) { return renderGroup(context, key, data, config); } return renderTag(context, key, data, config); @@ -487,7 +471,7 @@ function renderGroup(context, key, data, config) { if (isArray(data)) { { // 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) return []; @@ -513,7 +497,7 @@ function renderGroup(context, key, data, config) { function renderTag(context, key, data, config = {}, groupConfig) { // console.info('renderTag', key, data, config, groupConfig) 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)) { return data .map((child) => { @@ -522,7 +506,7 @@ function renderTag(context, key, data, config = {}, groupConfig) { .flat(); } const { tag = config.tag || key } = data; - let content; + let content = ''; let hasChilds = false; let isRaw = false; if (isString(data)) { @@ -574,24 +558,24 @@ function renderTag(context, key, data, config = {}, groupConfig) { content = getSlotContent(context, slotName, content, data); } else { - const contentAsAttribute = getConfig('contentAsAttribute'); - let valueAttribute = config.valueAttribute; + const contentAsAttribute = !!getTagConfig('contentAsAttribute'); + let { valueAttribute } = config; if (!valueAttribute && contentAsAttribute) { - const tagAttributes = getConfig('attributes'); - valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; + const [tagAttribute] = getTagConfig('attributes'); + valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute; } if (!valueAttribute) { content = getSlotContent(context, slotName, content, data); } else { - if (!config.nameless) { - const keyAttribute = getConfig('keyAttribute'); + const { nameless, keyAttribute } = config; + if (!nameless) { if (keyAttribute) { attributes[keyAttribute] = fullName; } } attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); - content = undefined; + content = ''; } } const finalTag = groupConfig && groupConfig.tagNamespace @@ -601,22 +585,22 @@ function renderTag(context, key, data, config = {}, groupConfig) { // console.log(' ATTRIBUTES', attributes) // console.log(' CONTENT', content) // // console.log(data, attributes, config) - let vnode; - if (isRaw) { + if (isRaw && 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 { to: data.to, vnode }; } -function renderAttributes(context, key, data, config = {}) { +function renderAttributes(context, key, data, config) { // console.info('renderAttributes', key, data, config) const { attributesFor } = config; + if (!attributesFor) { + return; + } { // render attributes in a placeholder vnode so Vue // will render the string for us @@ -627,19 +611,22 @@ function renderAttributes(context, key, data, config = {}) { } } function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { - if (!slots || !slots[slotName]) { + const slot = slots && slots[slotName]; + if (!slot) { return content; } - const slotProps = { + const slotScopeProps = { content, metainfo }; 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) { - return slotContent[0].children; + const { children } = slotContent[0]; + return children ? children.toString() : ''; } return content; } @@ -650,15 +637,18 @@ const PolySymbol = (name) => hasSymbol ? Symbol( '[vue-meta]: ' + name ) : ( '[vue-meta]: ' ) + name; -const metaInfoKey = PolySymbol( 'metainfo' ); +const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' ); function getCurrentManager(vm) { if (!vm) { - vm = vue.getCurrentInstance(); + vm = vue.getCurrentInstance() || undefined; + } + if (!vm) { + return undefined; } return vm.appContext.config.globalProperties.$metaManager; } -function useMeta(obj, manager) { +function useMeta(source, manager) { const vm = vue.getCurrentInstance(); if (!manager && vm) { manager = getCurrentManager(vm); @@ -667,10 +657,10 @@ function useMeta(obj, manager) { // oopsydoopsy throw new Error('No manager or current instance'); } - return manager.addMeta(obj, vm || undefined); + return manager.addMeta(source, vm || undefined); } -function useMetainfo() { - return vue.inject(metaInfoKey); +function useActiveMeta() { + return vue.inject(metaActiveKey); } const MetainfoImpl = vue.defineComponent({ @@ -690,23 +680,20 @@ const Metainfo = MetainfoImpl; const ssrAttribute = 'data-vm-ssr'; const active = vue.reactive({}); -function addVnode(teleports, to, _vnodes) { - const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); - { - // dont add ssrAttribute for attribute vnode placeholder - if (!to.endsWith('Attrs')) { - vnodes.forEach((vnode) => { - if (!vnode.props) { - vnode.props = {}; - } - vnode.props[ssrAttribute] = true; - }); - } +function addVnode(teleports, to, vnodes) { + const nodes = (isArray(vnodes) ? vnodes : [vnodes]); + if (!to.endsWith('Attrs')) { + nodes.forEach((vnode) => { + if (!vnode.props) { + vnode.props = {}; + } + vnode.props[ssrAttribute] = true; + }); } if (!teleports[to]) { teleports[to] = []; } - teleports[to].push(...vnodes); + teleports[to].push(...nodes); } function createMetaManager(config, resolver) { const resolve = (options, contexts, active, key, pathSegments) => { @@ -722,9 +709,12 @@ function createMetaManager(config, resolver) { install(app) { app.component('Metainfo', Metainfo); app.config.globalProperties.$metaManager = manager; - app.provide(metaInfoKey, active); + app.provide(metaActiveKey, active); }, addMeta(metaObj, vm) { + if (!vm) { + vm = vue.getCurrentInstance() || undefined; + } const resolveContext = { vm }; if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { resolver.setup(resolveContext); @@ -744,21 +734,34 @@ function createMetaManager(config, resolver) { const teleports = {}; for (const key in active) { const config = this.config[key] || {}; - const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); - if (!vnode) { + let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config); + if (!renderedNodes) { continue; } - const vnodes = isArray(vnode) ? vnode : [vnode]; - const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); - for (const { to, vnode } of vnodes) { - addVnode(teleports, to || defaultTo, vnode); + if (!isArray(renderedNodes)) { + renderedNodes = [renderedNodes]; + } + let defaultTo = key !== 'base' && active[key].to; + if (!defaultTo && 'to' in config) { + defaultTo = config.to; + } + if (!defaultTo && 'attributesFor' in config) { + defaultTo = key; + } + for (const { to, vnode } of renderedNodes) { + addVnode(teleports, to || defaultTo || 'head', vnode); } } if (slots) { - for (const tag in slots) { - const slotFn = slots[tag]; - if (isFunction(slotFn)) { - addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); + for (const slotName in slots) { + const tagName = slotName === 'default' ? 'head' : slotName; + // Only teleport the contents of head/body slots + if (tagName !== 'head' && tagName !== 'body') { + continue; + } + const slot = slots[slotName]; + if (isFunction(slot)) { + addVnode(teleports, tagName, slot({ metainfo: active })); } } } @@ -770,7 +773,7 @@ function createMetaManager(config, resolver) { 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'); async function renderToStringWithMeta(app) { const ctx = {}; @@ -797,5 +800,5 @@ exports.defaultConfig = defaultConfig; exports.getCurrentManager = getCurrentManager; exports.renderToStringWithMeta = renderToStringWithMeta; exports.resolveOption = resolveOption; +exports.useActiveMeta = useActiveMeta; exports.useMeta = useMeta; -exports.useMetainfo = useMetainfo; diff --git a/dist/vue-meta.cjs.prod.js b/dist/vue-meta.cjs.prod.js index 52f30e0..d166e13 100644 --- a/dist/vue-meta.cjs.prod.js +++ b/dist/vue-meta.cjs.prod.js @@ -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 - * - * TODO: we probably dont need all attributes */ const tags = { title: { @@ -166,24 +145,34 @@ const tags = { } }; -function getConfigByKey(tagOrName, key, config) { - if (config && key in config) { - return config[key]; - } - if (isArray(tagOrName)) { - for (const name of tagOrName) { - if (name && name in tags) { - return tags[name][key]; - } +function getTagConfigItem(tagOrName, key) { + for (const name of tagOrName) { + const tag = tags[name]; + if (name && tag) { + return tag[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 const IS_PROXY = Symbol('kIsProxy'); const PROXY_SOURCES = Symbol('kProxySources'); @@ -443,41 +432,36 @@ const createMergedObject = (resolve, active = {}) => { sources }; const compute = () => recompute(context); - const addSource = (source, resolveContext, recompute = false) => { - const proxy = createProxy(context, source, resolveContext || {}); - if (recompute) { - compute(); - } - 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); + return { + context, + compute, + addSource: (source, resolveContext, recompute = false) => { + const proxy = createProxy(context, source, resolveContext || {}); if (recompute) { 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) { // console.info('renderMeta', key, data, config) - if (config.attributesFor) { + if ('attributesFor' in config) { return renderAttributes(context, key, data, config); } - if (config.group) { + if ('group' in config) { return renderGroup(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) { // console.info('renderTag', key, data, config, groupConfig) 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)) { return data .map((child) => { @@ -518,7 +502,7 @@ function renderTag(context, key, data, config = {}, groupConfig) { .flat(); } const { tag = config.tag || key } = data; - let content; + let content = ''; let hasChilds = false; let isRaw = false; if (isString(data)) { @@ -570,24 +554,24 @@ function renderTag(context, key, data, config = {}, groupConfig) { content = getSlotContent(context, slotName, content, data); } else { - const contentAsAttribute = getConfig('contentAsAttribute'); - let valueAttribute = config.valueAttribute; + const contentAsAttribute = !!getTagConfig('contentAsAttribute'); + let { valueAttribute } = config; if (!valueAttribute && contentAsAttribute) { - const tagAttributes = getConfig('attributes'); - valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; + const [tagAttribute] = getTagConfig('attributes'); + valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute; } if (!valueAttribute) { content = getSlotContent(context, slotName, content, data); } else { - if (!config.nameless) { - const keyAttribute = getConfig('keyAttribute'); + const { nameless, keyAttribute } = config; + if (!nameless) { if (keyAttribute) { attributes[keyAttribute] = fullName; } } attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); - content = undefined; + content = ''; } } const finalTag = groupConfig && groupConfig.tagNamespace @@ -597,22 +581,22 @@ function renderTag(context, key, data, config = {}, groupConfig) { // console.log(' ATTRIBUTES', attributes) // console.log(' CONTENT', content) // // console.log(data, attributes, config) - let vnode; - if (isRaw) { + if (isRaw && 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 { to: data.to, vnode }; } -function renderAttributes(context, key, data, config = {}) { +function renderAttributes(context, key, data, config) { // console.info('renderAttributes', key, data, config) const { attributesFor } = config; + if (!attributesFor) { + return; + } { // render attributes in a placeholder vnode so Vue // will render the string for us @@ -623,19 +607,22 @@ function renderAttributes(context, key, data, config = {}) { } } function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { - if (!slots || !slots[slotName]) { + const slot = slots && slots[slotName]; + if (!slot) { return content; } - const slotProps = { + const slotScopeProps = { content, metainfo }; 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) { - return slotContent[0].children; + const { children } = slotContent[0]; + return children ? children.toString() : ''; } return content; } @@ -646,15 +633,18 @@ const PolySymbol = (name) => hasSymbol ? Symbol( name) : ( '_vm_') + name; -const metaInfoKey = PolySymbol( 'mi'); +const metaActiveKey = /*#__PURE__*/ PolySymbol( 'am'); function getCurrentManager(vm) { if (!vm) { - vm = vue.getCurrentInstance(); + vm = vue.getCurrentInstance() || undefined; + } + if (!vm) { + return undefined; } return vm.appContext.config.globalProperties.$metaManager; } -function useMeta(obj, manager) { +function useMeta(source, manager) { const vm = vue.getCurrentInstance(); if (!manager && vm) { manager = getCurrentManager(vm); @@ -663,10 +653,10 @@ function useMeta(obj, manager) { // oopsydoopsy throw new Error('No manager or current instance'); } - return manager.addMeta(obj, vm || undefined); + return manager.addMeta(source, vm || undefined); } -function useMetainfo() { - return vue.inject(metaInfoKey); +function useActiveMeta() { + return vue.inject(metaActiveKey); } const MetainfoImpl = vue.defineComponent({ @@ -686,23 +676,20 @@ const Metainfo = MetainfoImpl; const ssrAttribute = 'data-vm-ssr'; const active = vue.reactive({}); -function addVnode(teleports, to, _vnodes) { - const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); - { - // dont add ssrAttribute for attribute vnode placeholder - if (!to.endsWith('Attrs')) { - vnodes.forEach((vnode) => { - if (!vnode.props) { - vnode.props = {}; - } - vnode.props[ssrAttribute] = true; - }); - } +function addVnode(teleports, to, vnodes) { + const nodes = (isArray(vnodes) ? vnodes : [vnodes]); + if (!to.endsWith('Attrs')) { + nodes.forEach((vnode) => { + if (!vnode.props) { + vnode.props = {}; + } + vnode.props[ssrAttribute] = true; + }); } if (!teleports[to]) { teleports[to] = []; } - teleports[to].push(...vnodes); + teleports[to].push(...nodes); } function createMetaManager(config, resolver) { const resolve = (options, contexts, active, key, pathSegments) => { @@ -718,9 +705,12 @@ function createMetaManager(config, resolver) { install(app) { app.component('Metainfo', Metainfo); app.config.globalProperties.$metaManager = manager; - app.provide(metaInfoKey, active); + app.provide(metaActiveKey, active); }, addMeta(metaObj, vm) { + if (!vm) { + vm = vue.getCurrentInstance() || undefined; + } const resolveContext = { vm }; if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { resolver.setup(resolveContext); @@ -740,21 +730,34 @@ function createMetaManager(config, resolver) { const teleports = {}; for (const key in active) { const config = this.config[key] || {}; - const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); - if (!vnode) { + let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config); + if (!renderedNodes) { continue; } - const vnodes = isArray(vnode) ? vnode : [vnode]; - const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); - for (const { to, vnode } of vnodes) { - addVnode(teleports, to || defaultTo, vnode); + if (!isArray(renderedNodes)) { + renderedNodes = [renderedNodes]; + } + let defaultTo = key !== 'base' && active[key].to; + if (!defaultTo && 'to' in config) { + defaultTo = config.to; + } + if (!defaultTo && 'attributesFor' in config) { + defaultTo = key; + } + for (const { to, vnode } of renderedNodes) { + addVnode(teleports, to || defaultTo || 'head', vnode); } } if (slots) { - for (const tag in slots) { - const slotFn = slots[tag]; - if (isFunction(slotFn)) { - addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); + for (const slotName in slots) { + const tagName = slotName === 'default' ? 'head' : slotName; + // Only teleport the contents of head/body slots + if (tagName !== 'head' && tagName !== 'body') { + continue; + } + const slot = slots[slotName]; + if (isFunction(slot)) { + addVnode(teleports, tagName, slot({ metainfo: active })); } } } @@ -766,7 +769,7 @@ function createMetaManager(config, resolver) { 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'); async function renderToStringWithMeta(app) { const ctx = {}; @@ -793,5 +796,5 @@ exports.defaultConfig = defaultConfig; exports.getCurrentManager = getCurrentManager; exports.renderToStringWithMeta = renderToStringWithMeta; exports.resolveOption = resolveOption; +exports.useActiveMeta = useActiveMeta; exports.useMeta = useMeta; -exports.useMetainfo = useMetainfo; diff --git a/dist/vue-meta.d.ts b/dist/vue-meta.d.ts new file mode 100644 index 0000000..9cc6781 --- /dev/null +++ b/dist/vue-meta.d.ts @@ -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; +declare type ResolveContext = {}; +declare type ResolveMethod = (options: Array, contexts: Array, 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; +} +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; +}; +/** + * @internal + */ +declare type MetaTeleports = { + [key: string]: Array; +}; +/** + * @internal + */ +interface MetaRenderContext { + slots?: Slots; + metainfo: MetaActive; +} +/** + * @internal + */ +interface MetaGroupConfig { + group: string; + data: Array | 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; +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 }; diff --git a/dist/vue-meta.esm-browser.js b/dist/vue-meta.esm-browser.js index cfbaf71..44c986d 100644 --- a/dist/vue-meta.esm-browser.js +++ b/dist/vue-meta.esm-browser.js @@ -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 - * - * TODO: we probably dont need all attributes */ const tags = { title: { @@ -162,24 +141,34 @@ const tags = { } }; -function getConfigByKey(tagOrName, key, config) { - if (config && key in config) { - return config[key]; - } - if (isArray(tagOrName)) { - for (const name of tagOrName) { - if (name && name in tags) { - return tags[name][key]; - } +function getTagConfigItem(tagOrName, key) { + for (const name of tagOrName) { + const tag = tags[name]; + if (name && tag) { + return tag[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 const IS_PROXY = Symbol('kIsProxy'); const PROXY_SOURCES = Symbol('kProxySources'); @@ -439,42 +428,37 @@ const createMergedObject = (resolve, active = {}) => { sources }; const compute = () => recompute(context); - const addSource = (source, resolveContext, recompute = false) => { - const proxy = createProxy(context, source, resolveContext || {}); - if (recompute) { - compute(); - } - 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); + return { + context, + compute, + addSource: (source, resolveContext, recompute = false) => { + const proxy = createProxy(context, source, resolveContext || {}); if (recompute) { 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 = {}; function renderMeta(context, key, data, config) { // console.info('renderMeta', key, data, config) - if (config.attributesFor) { + if ('attributesFor' in config) { return renderAttributes(context, key, data, config); } - if (config.group) { + if ('group' in config) { return renderGroup(context, key, data, config); } return renderTag(context, key, data, config); @@ -484,7 +468,7 @@ function renderGroup(context, key, data, config) { if (isArray(data)) { { // 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) return []; @@ -510,7 +494,7 @@ function renderGroup(context, key, data, config) { function renderTag(context, key, data, config = {}, groupConfig) { // console.info('renderTag', key, data, config, groupConfig) 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)) { return data .map((child) => { @@ -519,7 +503,7 @@ function renderTag(context, key, data, config = {}, groupConfig) { .flat(); } const { tag = config.tag || key } = data; - let content; + let content = ''; let hasChilds = false; let isRaw = false; if (isString(data)) { @@ -571,24 +555,24 @@ function renderTag(context, key, data, config = {}, groupConfig) { content = getSlotContent(context, slotName, content, data); } else { - const contentAsAttribute = getConfig('contentAsAttribute'); - let valueAttribute = config.valueAttribute; + const contentAsAttribute = !!getTagConfig('contentAsAttribute'); + let { valueAttribute } = config; if (!valueAttribute && contentAsAttribute) { - const tagAttributes = getConfig('attributes'); - valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; + const [tagAttribute] = getTagConfig('attributes'); + valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute; } if (!valueAttribute) { content = getSlotContent(context, slotName, content, data); } else { - if (!config.nameless) { - const keyAttribute = getConfig('keyAttribute'); + const { nameless, keyAttribute } = config; + if (!nameless) { if (keyAttribute) { attributes[keyAttribute] = fullName; } } attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); - content = undefined; + content = ''; } } const finalTag = groupConfig && groupConfig.tagNamespace @@ -598,32 +582,32 @@ function renderTag(context, key, data, config = {}, groupConfig) { // console.log(' ATTRIBUTES', attributes) // console.log(' CONTENT', content) // // console.log(data, attributes, config) - let vnode; - if (isRaw) { + if (isRaw && 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 { to: data.to, vnode }; } -function renderAttributes(context, key, data, config = {}) { +function renderAttributes(context, key, data, config) { // console.info('renderAttributes', key, data, config) const { attributesFor } = config; + if (!attributesFor) { + return; + } if (!cachedElements[attributesFor]) { const [el, el2] = Array.from(document.querySelectorAll(attributesFor)); if ( !el) { // eslint-disable-next-line no-console - console.error('Could not find element with selector', attributesFor, ', won\'t render attributes'); + console.error('Could not find element for selector', attributesFor, ', won\'t render attributes'); return; } if ( el2) { // eslint-disable-next-line no-console - console.warn('Found multiple elements with selector', attributesFor); + console.warn('Found multiple elements for selector', attributesFor); } cachedElements[attributesFor] = { el, @@ -633,7 +617,7 @@ function renderAttributes(context, key, data, config = {}) { const { el, attrs } = cachedElements[attributesFor]; for (const attr in data) { const content = getSlotContent(context, `${key}(${attr})`, data[attr], data); - el.setAttribute(attr, `${content || ''}`); + el.setAttribute(attr, content || ''); if (!attrs.includes(attr)) { attrs.push(attr); } @@ -644,19 +628,22 @@ function renderAttributes(context, key, data, config = {}) { } } function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { - if (!slots || !slots[slotName]) { + const slot = slots && slots[slotName]; + if (!slot) { return content; } - const slotProps = { + const slotScopeProps = { content, metainfo }; 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) { - return slotContent[0].children; + const { children } = slotContent[0]; + return children ? children.toString() : ''; } return content; } @@ -667,15 +654,18 @@ const PolySymbol = (name) => hasSymbol ? Symbol( '[vue-meta]: ' + name ) : ( '[vue-meta]: ' ) + name; -const metaInfoKey = PolySymbol( 'metainfo' ); +const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' ); function getCurrentManager(vm) { if (!vm) { - vm = getCurrentInstance(); + vm = getCurrentInstance() || undefined; + } + if (!vm) { + return undefined; } return vm.appContext.config.globalProperties.$metaManager; } -function useMeta(obj, manager) { +function useMeta(source, manager) { const vm = getCurrentInstance(); if (!manager && vm) { manager = getCurrentManager(vm); @@ -684,10 +674,10 @@ function useMeta(obj, manager) { // oopsydoopsy throw new Error('No manager or current instance'); } - return manager.addMeta(obj, vm || undefined); + return manager.addMeta(source, vm || undefined); } -function useMetainfo() { - return inject(metaInfoKey); +function useActiveMeta() { + return inject(metaActiveKey); } const MetainfoImpl = defineComponent({ @@ -707,22 +697,24 @@ const Metainfo = MetainfoImpl; const ssrAttribute = 'data-vm-ssr'; const active = reactive({}); -function addVnode(teleports, to, _vnodes) { - const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); +function addVnode(teleports, to, vnodes) { + const nodes = (isArray(vnodes) ? vnodes : [vnodes]); { // 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) { - vnodes.splice(idx, 1); + nodes.splice(idx, 1); } }); + // only add ssrAttribute's for real meta tags } if (!teleports[to]) { teleports[to] = []; } - teleports[to].push(...vnodes); + teleports[to].push(...nodes); } function createMetaManager(config, resolver) { + let cleanedUpSsr = false; const resolve = (options, contexts, active, key, pathSegments) => { if (isFunction(resolver)) { return resolver(options, contexts, active, key, pathSegments); @@ -730,16 +722,18 @@ function createMetaManager(config, resolver) { return resolver.resolve(options, contexts, active, key, pathSegments); }; const { addSource, delSource } = createMergedObject(resolve, active); - let cleanedUpSsr = false; // TODO: validate resolver const manager = { config, install(app) { app.component('Metainfo', Metainfo); app.config.globalProperties.$metaManager = manager; - app.provide(metaInfoKey, active); + app.provide(metaActiveKey, active); }, addMeta(metaObj, vm) { + if (!vm) { + vm = getCurrentInstance() || undefined; + } const resolveContext = { vm }; if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { resolver.setup(resolveContext); @@ -759,9 +753,9 @@ function createMetaManager(config, resolver) { // cleanup ssr tags if not yet done if ( !cleanedUpSsr) { cleanedUpSsr = true; - // Listen for DOM loaded because tags in the body couldnt be loaded - // yet once the manager does it first render - // (preferable there should only be one render on hydration) + // Listen for DOM loaded because tags in the body couldnt + // have loaded yet once the manager does it first render + // (preferable there should only be one meta render on hydration) window.addEventListener('DOMContentLoaded', () => { const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`); if (ssrTags && ssrTags.length) { @@ -772,21 +766,34 @@ function createMetaManager(config, resolver) { const teleports = {}; for (const key in active) { const config = this.config[key] || {}; - const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); - if (!vnode) { + let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config); + if (!renderedNodes) { continue; } - const vnodes = isArray(vnode) ? vnode : [vnode]; - const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); - for (const { to, vnode } of vnodes) { - addVnode(teleports, to || defaultTo, vnode); + if (!isArray(renderedNodes)) { + renderedNodes = [renderedNodes]; + } + let defaultTo = key !== 'base' && active[key].to; + if (!defaultTo && 'to' in config) { + defaultTo = config.to; + } + if (!defaultTo && 'attributesFor' in config) { + defaultTo = key; + } + for (const { to, vnode } of renderedNodes) { + addVnode(teleports, to || defaultTo || 'head', vnode); } } if (slots) { - for (const tag in slots) { - const slotFn = slots[tag]; - if (isFunction(slotFn)) { - addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); + for (const slotName in slots) { + const tagName = slotName === 'default' ? 'head' : slotName; + // Only teleport the contents of head/body slots + if (tagName !== 'head' && tagName !== 'body') { + continue; + } + const slot = slots[slotName]; + if (isFunction(slot)) { + addVnode(teleports, tagName, slot({ metainfo: active })); } } } @@ -798,4 +805,4 @@ function createMetaManager(config, resolver) { return manager; } -export { createMetaManager, deepest as deepestResolver, defaultConfig, getCurrentManager, resolveOption, useMeta, useMetainfo }; +export { createMetaManager, deepest as deepestResolver, defaultConfig, getCurrentManager, resolveOption, useActiveMeta, useMeta }; diff --git a/dist/vue-meta.esm-browser.min.js b/dist/vue-meta.esm-browser.min.js index bb57e94..38096a4 100644 --- a/dist/vue-meta.esm-browser.min.js +++ b/dist/vue-meta.esm-browser.min.js @@ -5,4 +5,4 @@ * - All the amazing contributors * @license MIT */ -import{markRaw as t,h as e,getCurrentInstance as o,inject as r,defineComponent as n,reactive as s,onUnmounted as c,Teleport as i,Comment as a}from"vue";const u=t=>(e,o)=>{let r=-1;if(o.reduce(((e,o,n)=>{const s=t(e,o);return s!==e?(r=n,s):e}),void 0),r>-1)return e[r]};const f=u(((t,e)=>{const{depth:o}=e;if(!t||o>t)return t}));var l=Object.freeze({__proto__:null,setup:function(t){let e=0;if(t.vm){let{vm:o}=t;do{o.parent&&(e++,o=o.parent)}while(o&&o.parent&&o!==o.root)}t.depth=e},resolve:f});const p={body:{tag:"script",to:"body"},base:{valueAttribute:"href"},charset:{tag:"meta",nameless:!0,valueAttribute:"charset"},description:{tag:"meta"},og:{group:!0,namespacedAttribute:!0,tag:"meta",keyAttribute:"property"},twitter:{group:!0,namespacedAttribute:!0,tag:"meta"},htmlAttrs:{attributesFor:"html"},headAttrs:{attributesFor:"head"},bodyAttrs:{attributesFor:"body"}};"production"===process.env.NODE_ENV||Object.freeze({}),"production"===process.env.NODE_ENV||Object.freeze([]);const d=Array.isArray,m=t=>"function"==typeof t,b=t=>"string"==typeof t,y=t=>null!==t&&"object"==typeof t,g=Object.prototype.toString,h=t=>"[object Object]"===g.call(t),v={title:{attributes:!1},base:{contentAsAttribute:!0,attributes:["href","target"]},meta:{contentAsAttribute:!0,keyAttribute:"name",attributes:["content","name","http-equiv","charset"]},link:{contentAsAttribute:!0,attributes:["href","crossorigin","rel","media","integrity","hreflang","type","referrerpolicy","sizes","imagesrcset","imagesizes","as","color"]},style:{attributes:["media"]},script:{attributes:["src","type","nomodule","async","defer","crossorigin","integrity","referrerpolicy"]},noscript:{attributes:!1}};const A=Symbol("kIsProxy"),S=Symbol("kProxySources"),k=Symbol("kProxyTarget"),N=Symbol("kResolveContext");function w(t){if(d(t))return t.map(w);if(y(t)){const e={};for(const o in t)e[o]="context"===o?t[o]:w(t[o]);return e}return t}const j=(t,e,o)=>{const r=[];for(const n of t)e in n&&(r.push(n[e]),o&&o(n));return r},O=(t,e,o,r=[])=>{if(r.length||(o||(o=t.active),e||(e=t.sources)),!o||!e)return;const n=((t,...e)=>{const o=t?Object.keys(t):[];if(e)for(const t of e)if(t&&y(t))for(const e in t)o.includes(e)||o.push(e);return o})(...e),s=Object.keys(o);for(const t of s)n.includes(t)||delete o[t];for(const s of n){if(h(e[0][s])){o[s]||(o[s]={});const n=[];for(const t of e)s in t&&n.push(t[s]);O(t,n,o[s],[...r,s]);continue}!o[s]&&d(e[0][s])&&(o[s]=[]);const n=[],c=j(e,s,(t=>n.push(t[N])));let i=t.resolve(c,n,o[s],s,r);h(i)&&(i=w(i)),o[s]=i}},$=(e,o,r,n=[])=>{const s=x(e,r,n),c=t(new Proxy(o,s));return!n.length&&e.sources&&e.sources.push(c),c},x=(t,e,o=[])=>({get:(r,n,s)=>{if(n===A)return!0;if(n===S)return t.sources;if(n===k)return r;if(n===N)return e;let c=Reflect.get(r,n,s);if(!y(c))return c;if(!c[A]){const s=[...o,n];c=$(t,c,e,s),r[n]=c}return c},set:(e,r,n)=>{const s=Reflect.set(e,r,n);if(s){const n=d(e);let c,i=!1,{sources:a,active:u}=t,f=0;for(const t of o){if(a=j(a,t),n&&f===o.length-1){c=t;break}d(u)&&(i=!0),u=u[t],f++}if(i)return O(t),s;let l,p=[];n?(l=a,p=a.map((t=>t[N]))):l=j(a,r,(t=>p.push(t[N])));let m=t.resolve(l,p,u,r,o);h(m)&&(m=w(m)),n&&c?u[c]=m:u[r]=m}return s},deleteProperty:(e,r)=>{const n=Reflect.deleteProperty(e,r);if(n){const n=d(e);let s,c=t.sources,i=t.active,a=0;for(const t of o){if(c=c.map((e=>e[t])),n&&a===o.length-1){s=t;break}i=i[t],a++}if(c.some((t=>r in t))){let e,a=[];n?(e=c,a=c.map((t=>t[N]))):e=j(c,r,(t=>a.push(t[N])));let u=t.resolve(e,a,i,r,o);h(u)&&(u=w(u)),n&&s?i[s]=u:i[r]=u}else delete i[r]}return n}}),E={};function M(t,e,o,r){return r.attributesFor?function(t,e,o,r={}){const{attributesFor:n}=r;if(!E[n]){const[t,e]=Array.from(document.querySelectorAll(n));if(!t)return void console.error("Could not find element with selector",n,", won't render attributes");e&&console.warn("Found multiple elements with selector",n),E[n]={el:t,attrs:[]}}const{el:s,attrs:c}=E[n];for(const r in o){const n=F(t,`${e}(${r})`,o[r],o);s.setAttribute(r,`${n||""}`),c.includes(r)||c.push(r)}const i=c.filter((t=>!o[t]));for(const t of i)s.removeAttribute(t)}(t,e,o,r):r.group?function(t,e,o,r){if(d(o))return 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"),[];return Object.keys(o).map((n=>{const s={group:e,data:o};if(r.namespaced)s.tagNamespace=!0===r.namespaced?e:r.namespaced;else if(r.namespacedAttribute){const t=!0===r.namespacedAttribute?e:r.namespacedAttribute;s.fullName=`${t}:${n}`,s.slotName=`${t}(${n})`}return P(t,e,o[n],r,s)})).flat()}(t,e,o,r):P(t,e,o,r)}function P(t,o,r,n={},s){const c=["content","json","rawContent"],i=t=>function(t,e,o){if(o&&e in o)return o[e];if(d(t)){for(const o of t)if(o&&o in v)return v[o][e]}else if(t in v)return v[t][e]}([a,n.tag],t,n);if(d(r))return r.map((e=>P(t,o,e,n,s))).flat();const{tag:a=n.tag||o}=r;let u,f=!1,l=!1;if(b(r))u=r;else if(r.children&&d(r.children))f=!0,u=r.children.map((e=>{const r=P(t,o,e,n,s);return d(r)?r.map((({vnode:t})=>t)):r.vnode}));else{let t=0;for(const e of c){if(!u&&r[e]){u=1===t?JSON.stringify(r[e]):r[e],l=t>1;break}t++}}const p=s&&s.fullName||o,m=s&&s.slotName||o;let{attrs:y}=r;if(y||"object"!=typeof r)y||(y={});else{y={...r},delete y.tag,delete y.children,delete y.to;for(const t of c)delete y[t]}if(f)u=F(t,m,u,r);else{const e=i("contentAsAttribute");let o=n.valueAttribute;if(!o&&e){const t=i("attributes");o=b(e)?e:t[0]}if(o){if(!n.nameless){const t=i("keyAttribute");t&&(y[t]=p)}y[o]=F(t,m,y[o]||u,s),u=void 0}else u=F(t,m,u,r)}const g=s&&s.tagNamespace?`${s.tagNamespace}:${a}`:a;let h;return l?(y.innerHTML=u,h=e(g,y)):h=e(g,y,u),{to:r.to,vnode:h}}function F({metainfo:t,slots:e},o,r,n){if(!e||!e[o])return r;const s={content:r,metainfo:t};n&&n.group&&(s[n.group]=n.data);const c=e[o](s);return c&&c.length?c[0].children:r}const C="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,_=(z="metainfo",C?Symbol("[vue-meta]: "+z):"[vue-meta]: "+z);var z;function R(t){return t||(t=o()),t.appContext.config.globalProperties.$metaManager}function q(t,e){const r=o();if(!e&&r&&(e=R(r)),!e)throw new Error("No manager or current instance");return e.addMeta(t,r||void 0)}function D(){return r(_)}const I=n({name:"Metainfo",inheritAttrs:!1,setup:(t,{slots:e})=>()=>{const t=R();if(t)return t.render({slots:e})}}),L=s({});function T(t,e,o){const r=d(o)?o:[o];r.forEach(((t,e)=>{t.type===a&&r.splice(e,1)})),t[e]||(t[e]=[]),t[e].push(...r)}function V(t,o){const{addSource:r,delSource:n}=((t,e={})=>{const o=[];e||(e={});const r={active:e,resolve:t,sources:o},n=()=>O(r);return{context:r,active:e,resolve:t,sources:o,addSource:(t,e,o=!1)=>{const s=$(r,t,e||{});return o&&n(),s},delSource:(t,e=!0)=>{const r=o.findIndex((e=>e===t||e[k]===t));return r>-1&&(o.splice(r,1),e&&n(),!0)},compute:n}})(((t,e,r,n,s)=>m(o)?o(t,e,r,n,s):o.resolve(t,e,r,n,s)),L);let s=!1;const a={config:t,install(t){t.component("Metainfo",I),t.config.globalProperties.$metaManager=a,t.provide(_,L)},addMeta(t,e){const s={vm:e};o&&"setup"in o&&m(o.setup)&&o.setup(s);const i=r(t,s,!0),a=()=>n(i);return e&&c(a),{meta:i,unmount:a}},render({slots:t}={}){s||(s=!0,window.addEventListener("DOMContentLoaded",(()=>{const t=document.querySelectorAll("[data-vm-ssr]");t&&t.length&&Array.from(t).forEach((t=>t.parentNode&&t.parentNode.removeChild(t)))})));const o={};for(const e in L){const r=this.config[e]||{},n=M({metainfo:L,slots:t},e,L[e],r);if(!n)continue;const s=d(n)?n:[n],c="base"!==e&&L[e].to||r.to||(r.attributesFor?e:"head");for(const{to:t,vnode:e}of s)T(o,t||c,e)}if(t)for(const e in t){const r=t[e];m(r)&&T(o,"default"===e?"head":e,r({metainfo:L}))}return Object.keys(o).map((t=>e(i,{to:t},o[t])))}};return a}export{V as createMetaManager,l as deepestResolver,p as defaultConfig,R as getCurrentManager,u as resolveOption,q as useMeta,D as useMetainfo}; +import{markRaw as t,h as e,getCurrentInstance as o,inject as r,defineComponent as n,reactive as s,onUnmounted as c,Teleport as i,Comment as a}from"vue";const u=t=>(e,o)=>{let r=-1;if(o.reduce(((e,o,n)=>{const s=t(e,o);return s!==e?(r=n,s):e}),void 0),r>-1)return e[r]};const f=u(((t,e)=>{const{depth:o}=e;if(!t||o>t)return t}));var l=Object.freeze({__proto__:null,setup:function(t){let e=0;if(t.vm){let{vm:o}=t;do{o.parent&&(e++,o=o.parent)}while(o&&o.parent&&o!==o.root)}t.depth=e},resolve:f});const p={body:{tag:"script",to:"body"},base:{valueAttribute:"href"},charset:{tag:"meta",nameless:!0,valueAttribute:"charset"},description:{tag:"meta"},og:{group:!0,namespacedAttribute:!0,tag:"meta",keyAttribute:"property"},twitter:{group:!0,namespacedAttribute:!0,tag:"meta"},htmlAttrs:{attributesFor:"html"},headAttrs:{attributesFor:"head"},bodyAttrs:{attributesFor:"body"}},d={title:{attributes:!1},base:{contentAsAttribute:!0,attributes:["href","target"]},meta:{contentAsAttribute:!0,keyAttribute:"name",attributes:["content","name","http-equiv","charset"]},link:{contentAsAttribute:!0,attributes:["href","crossorigin","rel","media","integrity","hreflang","type","referrerpolicy","sizes","imagesrcset","imagesizes","as","color"]},style:{attributes:["media"]},script:{attributes:["src","type","nomodule","async","defer","crossorigin","integrity","referrerpolicy"]},noscript:{attributes:!1}};"production"===process.env.NODE_ENV||Object.freeze({}),"production"===process.env.NODE_ENV||Object.freeze([]);const m=Array.isArray,b=t=>"function"==typeof t,g=t=>"string"==typeof t,y=t=>null!==t&&"object"==typeof t,h=Object.prototype.toString,v=t=>"[object Object]"===h.call(t),A=Symbol("kIsProxy"),S=Symbol("kProxySources"),k=Symbol("kProxyTarget"),N=Symbol("kResolveContext");function j(t){if(m(t))return t.map(j);if(y(t)){const e={};for(const o in t)e[o]="context"===o?t[o]:j(t[o]);return e}return t}const O=(t,e,o)=>{const r=[];for(const n of t)e in n&&(r.push(n[e]),o&&o(n));return r},w=(t,e,o,r=[])=>{if(r.length||(o||(o=t.active),e||(e=t.sources)),!o||!e)return;const n=((t,...e)=>{const o=t?Object.keys(t):[];if(e)for(const t of e)if(t&&y(t))for(const e in t)o.includes(e)||o.push(e);return o})(...e),s=Object.keys(o);for(const t of s)n.includes(t)||delete o[t];for(const s of n){if(v(e[0][s])){o[s]||(o[s]={});const n=[];for(const t of e)s in t&&n.push(t[s]);w(t,n,o[s],[...r,s]);continue}!o[s]&&m(e[0][s])&&(o[s]=[]);const n=[],c=O(e,s,(t=>n.push(t[N])));let i=t.resolve(c,n,o[s],s,r);v(i)&&(i=j(i)),o[s]=i}},x=(e,o,r,n=[])=>{const s=$(e,r,n),c=t(new Proxy(o,s));return!n.length&&e.sources&&e.sources.push(c),c},$=(t,e,o=[])=>({get:(r,n,s)=>{if(n===A)return!0;if(n===S)return t.sources;if(n===k)return r;if(n===N)return e;let c=Reflect.get(r,n,s);if(!y(c))return c;if(!c[A]){const s=[...o,n];c=x(t,c,e,s),r[n]=c}return c},set:(e,r,n)=>{const s=Reflect.set(e,r,n);if(s){const n=m(e);let c,i=!1,{sources:a,active:u}=t,f=0;for(const t of o){if(a=O(a,t),n&&f===o.length-1){c=t;break}m(u)&&(i=!0),u=u[t],f++}if(i)return w(t),s;let l,p=[];n?(l=a,p=a.map((t=>t[N]))):l=O(a,r,(t=>p.push(t[N])));let d=t.resolve(l,p,u,r,o);v(d)&&(d=j(d)),n&&c?u[c]=d:u[r]=d}return s},deleteProperty:(e,r)=>{const n=Reflect.deleteProperty(e,r);if(n){const n=m(e);let s,c=t.sources,i=t.active,a=0;for(const t of o){if(c=c.map((e=>e[t])),n&&a===o.length-1){s=t;break}i=i[t],a++}if(c.some((t=>r in t))){let e,a=[];n?(e=c,a=c.map((t=>t[N]))):e=O(c,r,(t=>a.push(t[N])));let u=t.resolve(e,a,i,r,o);v(u)&&(u=j(u)),n&&s?i[s]=u:i[r]=u}else delete i[r]}return n}}),E={};function M(t,e,o,r){return"attributesFor"in r?function(t,e,o,r){const{attributesFor:n}=r;if(!n)return;if(!E[n]){const[t,e]=Array.from(document.querySelectorAll(n));if(!t)return void console.error("Could not find element for selector",n,", won't render attributes");e&&console.warn("Found multiple elements for selector",n),E[n]={el:t,attrs:[]}}const{el:s,attrs:c}=E[n];for(const r in o){const n=F(t,`${e}(${r})`,o[r],o);s.setAttribute(r,n||""),c.includes(r)||c.push(r)}const i=c.filter((t=>!o[t]));for(const t of i)s.removeAttribute(t)}(t,e,o,r):"group"in r?function(t,e,o,r){if(m(o))return console.warn("Specifying an array for group properties isnt supported"),[];return Object.keys(o).map((n=>{const s={group:e,data:o};if(r.namespaced)s.tagNamespace=!0===r.namespaced?e:r.namespaced;else if(r.namespacedAttribute){const t=!0===r.namespacedAttribute?e:r.namespacedAttribute;s.fullName=`${t}:${n}`,s.slotName=`${t}(${n})`}return P(t,e,o[n],r,s)})).flat()}(t,e,o,r):P(t,e,o,r)}function P(t,o,r,n={},s){const c=["content","json","rawContent"],i=t=>function(t,e){for(const o of t){const t=d[o];if(o&&t)return t[e]}}([a,n.tag],t);if(m(r))return r.map((e=>P(t,o,e,n,s))).flat();const{tag:a=n.tag||o}=r;let u="",f=!1,l=!1;if(g(r))u=r;else if(r.children&&m(r.children))f=!0,u=r.children.map((e=>{const r=P(t,o,e,n,s);return m(r)?r.map((({vnode:t})=>t)):r.vnode}));else{let t=0;for(const e of c){if(!u&&r[e]){u=1===t?JSON.stringify(r[e]):r[e],l=t>1;break}t++}}const p=s&&s.fullName||o,b=s&&s.slotName||o;let{attrs:y}=r;if(y||"object"!=typeof r)y||(y={});else{y={...r},delete y.tag,delete y.children,delete y.to;for(const t of c)delete y[t]}if(f)u=F(t,b,u,r);else{const e=!!i("contentAsAttribute");let{valueAttribute:o}=n;if(!o&&e){const[t]=i("attributes");o=g(e)?e:t}if(o){const{nameless:e,keyAttribute:r}=n;e||r&&(y[r]=p),y[o]=F(t,b,y[o]||u,s),u=""}else u=F(t,b,u,r)}const h=s&&s.tagNamespace?`${s.tagNamespace}:${a}`:a;l&&u&&(y.innerHTML=u);const v=e(h,y,u||void 0);return{to:r.to,vnode:v}}function F({metainfo:t,slots:e},o,r,n){const s=e&&e[o];if(!s)return r;const c={content:r,metainfo:t};if(n&&n.group){const{group:t,data:e}=n;c[t]=e}const i=s(c);if(i&&i.length){const{children:t}=i[0];return t?t.toString():""}return r}const _="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,C=(t=>_?Symbol("[vue-meta]: "+t):"[vue-meta]: "+t)("active_meta");function z(t){if(t||(t=o()||void 0),t)return t.appContext.config.globalProperties.$metaManager}function R(t,e){const r=o();if(!e&&r&&(e=z(r)),!e)throw new Error("No manager or current instance");return e.addMeta(t,r||void 0)}function q(){return r(C)}const D=n({name:"Metainfo",inheritAttrs:!1,setup:(t,{slots:e})=>()=>{const t=z();if(t)return t.render({slots:e})}}),L=s({});function T(t,e,o){const r=m(o)?o:[o];r.forEach(((t,e)=>{t.type===a&&r.splice(e,1)})),t[e]||(t[e]=[]),t[e].push(...r)}function I(t,r){let n=!1;const{addSource:s,delSource:a}=((t,e={})=>{const o=[];e||(e={});const r={active:e,resolve:t,sources:o},n=()=>w(r);return{context:r,compute:n,addSource:(t,e,o=!1)=>{const s=x(r,t,e||{});return o&&n(),s},delSource:(t,e=!0)=>{const r=o.findIndex((e=>e===t||e[k]===t));return r>-1&&(o.splice(r,1),e&&n(),!0)}}})(((t,e,o,n,s)=>b(r)?r(t,e,o,n,s):r.resolve(t,e,o,n,s)),L),u={config:t,install(t){t.component("Metainfo",D),t.config.globalProperties.$metaManager=u,t.provide(C,L)},addMeta(t,e){e||(e=o()||void 0);const n={vm:e};r&&"setup"in r&&b(r.setup)&&r.setup(n);const i=s(t,n,!0),u=()=>a(i);return e&&c(u),{meta:i,unmount:u}},render({slots:t}={}){n||(n=!0,window.addEventListener("DOMContentLoaded",(()=>{const t=document.querySelectorAll("[data-vm-ssr]");t&&t.length&&Array.from(t).forEach((t=>t.parentNode&&t.parentNode.removeChild(t)))})));const o={};for(const e in L){const r=this.config[e]||{};let n=M({metainfo:L,slots:t},e,L[e],r);if(!n)continue;m(n)||(n=[n]);let s="base"!==e&&L[e].to;!s&&"to"in r&&(s=r.to),!s&&"attributesFor"in r&&(s=e);for(const{to:t,vnode:e}of n)T(o,t||s||"head",e)}if(t)for(const e in t){const r="default"===e?"head":e;if("head"!==r&&"body"!==r)continue;const n=t[e];b(n)&&T(o,r,n({metainfo:L}))}return Object.keys(o).map((t=>e(i,{to:t},o[t])))}};return u}export{I as createMetaManager,l as deepestResolver,p as defaultConfig,z as getCurrentManager,u as resolveOption,q as useActiveMeta,R as useMeta}; diff --git a/dist/vue-meta.esm-bundler.js b/dist/vue-meta.esm-bundler.js index c0d0a7d..469e1e2 100644 --- a/dist/vue-meta.esm-bundler.js +++ b/dist/vue-meta.esm-bundler.js @@ -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 - * - * TODO: we probably dont need all attributes */ const tags = { title: { @@ -162,24 +141,34 @@ const tags = { } }; -function getConfigByKey(tagOrName, key, config) { - if (config && key in config) { - return config[key]; - } - if (isArray(tagOrName)) { - for (const name of tagOrName) { - if (name && name in tags) { - return tags[name][key]; - } +function getTagConfigItem(tagOrName, key) { + for (const name of tagOrName) { + const tag = tags[name]; + if (name && tag) { + return tag[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 const IS_PROXY = Symbol('kIsProxy'); const PROXY_SOURCES = Symbol('kProxySources'); @@ -439,41 +428,36 @@ const createMergedObject = (resolve, active = {}) => { sources }; const compute = () => recompute(context); - const addSource = (source, resolveContext, recompute = false) => { - const proxy = createProxy(context, source, resolveContext || {}); - if (recompute) { - compute(); - } - 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); + return { + context, + compute, + addSource: (source, resolveContext, recompute = false) => { + const proxy = createProxy(context, source, resolveContext || {}); if (recompute) { 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) { // console.info('renderMeta', key, data, config) - if (config.attributesFor) { + if ('attributesFor' in config) { return renderAttributes(context, key, data, config); } - if (config.group) { + if ('group' in config) { return renderGroup(context, key, data, config); } return renderTag(context, key, data, config); @@ -483,7 +467,7 @@ function renderGroup(context, key, data, config) { if (isArray(data)) { if ((process.env.NODE_ENV !== 'production')) { // 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) return []; @@ -509,7 +493,7 @@ function renderGroup(context, key, data, config) { function renderTag(context, key, data, config = {}, groupConfig) { // console.info('renderTag', key, data, config, groupConfig) 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)) { return data .map((child) => { @@ -518,7 +502,7 @@ function renderTag(context, key, data, config = {}, groupConfig) { .flat(); } const { tag = config.tag || key } = data; - let content; + let content = ''; let hasChilds = false; let isRaw = false; if (isString(data)) { @@ -570,24 +554,24 @@ function renderTag(context, key, data, config = {}, groupConfig) { content = getSlotContent(context, slotName, content, data); } else { - const contentAsAttribute = getConfig('contentAsAttribute'); - let valueAttribute = config.valueAttribute; + const contentAsAttribute = !!getTagConfig('contentAsAttribute'); + let { valueAttribute } = config; if (!valueAttribute && contentAsAttribute) { - const tagAttributes = getConfig('attributes'); - valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; + const [tagAttribute] = getTagConfig('attributes'); + valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute; } if (!valueAttribute) { content = getSlotContent(context, slotName, content, data); } else { - if (!config.nameless) { - const keyAttribute = getConfig('keyAttribute'); + const { nameless, keyAttribute } = config; + if (!nameless) { if (keyAttribute) { attributes[keyAttribute] = fullName; } } attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); - content = undefined; + content = ''; } } const finalTag = groupConfig && groupConfig.tagNamespace @@ -597,22 +581,22 @@ function renderTag(context, key, data, config = {}, groupConfig) { // console.log(' ATTRIBUTES', attributes) // console.log(' CONTENT', content) // // console.log(data, attributes, config) - let vnode; - if (isRaw) { + if (isRaw && 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 { to: data.to, vnode }; } -function renderAttributes(context, key, data, config = {}) { +function renderAttributes(context, key, data, config) { // console.info('renderAttributes', key, data, config) const { attributesFor } = config; + if (!attributesFor) { + return; + } { // render attributes in a placeholder vnode so Vue // will render the string for us @@ -623,19 +607,22 @@ function renderAttributes(context, key, data, config = {}) { } } function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { - if (!slots || !slots[slotName]) { + const slot = slots && slots[slotName]; + if (!slot) { return content; } - const slotProps = { + const slotScopeProps = { content, metainfo }; 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) { - return slotContent[0].children; + const { children } = slotContent[0]; + return children ? children.toString() : ''; } return content; } @@ -646,15 +633,18 @@ const PolySymbol = (name) => hasSymbol ? Symbol((process.env.NODE_ENV !== 'production') ? '[vue-meta]: ' + name : 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) { if (!vm) { - vm = getCurrentInstance(); + vm = getCurrentInstance() || undefined; + } + if (!vm) { + return undefined; } return vm.appContext.config.globalProperties.$metaManager; } -function useMeta(obj, manager) { +function useMeta(source, manager) { const vm = getCurrentInstance(); if (!manager && vm) { manager = getCurrentManager(vm); @@ -663,10 +653,10 @@ function useMeta(obj, manager) { // oopsydoopsy throw new Error('No manager or current instance'); } - return manager.addMeta(obj, vm || undefined); + return manager.addMeta(source, vm || undefined); } -function useMetainfo() { - return inject(metaInfoKey); +function useActiveMeta() { + return inject(metaActiveKey); } const MetainfoImpl = defineComponent({ @@ -686,23 +676,20 @@ const Metainfo = MetainfoImpl; const ssrAttribute = 'data-vm-ssr'; const active = reactive({}); -function addVnode(teleports, to, _vnodes) { - const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); - { - // dont add ssrAttribute for attribute vnode placeholder - if (!to.endsWith('Attrs')) { - vnodes.forEach((vnode) => { - if (!vnode.props) { - vnode.props = {}; - } - vnode.props[ssrAttribute] = true; - }); - } +function addVnode(teleports, to, vnodes) { + const nodes = (isArray(vnodes) ? vnodes : [vnodes]); + if (!to.endsWith('Attrs')) { + nodes.forEach((vnode) => { + if (!vnode.props) { + vnode.props = {}; + } + vnode.props[ssrAttribute] = true; + }); } if (!teleports[to]) { teleports[to] = []; } - teleports[to].push(...vnodes); + teleports[to].push(...nodes); } function createMetaManager(config, resolver) { const resolve = (options, contexts, active, key, pathSegments) => { @@ -718,9 +705,12 @@ function createMetaManager(config, resolver) { install(app) { app.component('Metainfo', Metainfo); app.config.globalProperties.$metaManager = manager; - app.provide(metaInfoKey, active); + app.provide(metaActiveKey, active); }, addMeta(metaObj, vm) { + if (!vm) { + vm = getCurrentInstance() || undefined; + } const resolveContext = { vm }; if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { resolver.setup(resolveContext); @@ -740,21 +730,34 @@ function createMetaManager(config, resolver) { const teleports = {}; for (const key in active) { const config = this.config[key] || {}; - const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); - if (!vnode) { + let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config); + if (!renderedNodes) { continue; } - const vnodes = isArray(vnode) ? vnode : [vnode]; - const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); - for (const { to, vnode } of vnodes) { - addVnode(teleports, to || defaultTo, vnode); + if (!isArray(renderedNodes)) { + renderedNodes = [renderedNodes]; + } + let defaultTo = key !== 'base' && active[key].to; + if (!defaultTo && 'to' in config) { + defaultTo = config.to; + } + if (!defaultTo && 'attributesFor' in config) { + defaultTo = key; + } + for (const { to, vnode } of renderedNodes) { + addVnode(teleports, to || defaultTo || 'head', vnode); } } if (slots) { - for (const tag in slots) { - const slotFn = slots[tag]; - if (isFunction(slotFn)) { - addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); + for (const slotName in slots) { + const tagName = slotName === 'default' ? 'head' : slotName; + // Only teleport the contents of head/body slots + if (tagName !== 'head' && tagName !== 'body') { + continue; + } + const slot = slots[slotName]; + if (isFunction(slot)) { + addVnode(teleports, tagName, slot({ metainfo: active })); } } } @@ -766,7 +769,7 @@ function createMetaManager(config, resolver) { 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'); async function renderToStringWithMeta(app) { const ctx = {}; @@ -787,4 +790,4 @@ async function renderToStringWithMeta(app) { 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 }; diff --git a/dist/vue-meta.global.js b/dist/vue-meta.global.js index 674ce59..928e3f8 100644 --- a/dist/vue-meta.global.js +++ b/dist/vue-meta.global.js @@ -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 - * - * TODO: we probably dont need all attributes */ const tags = { title: { @@ -163,24 +142,34 @@ var VueMeta = (function (exports, vue) { } }; - function getConfigByKey(tagOrName, key, config) { - if (config && key in config) { - return config[key]; - } - if (isArray(tagOrName)) { - for (const name of tagOrName) { - if (name && name in tags) { - return tags[name][key]; - } + function getTagConfigItem(tagOrName, key) { + for (const name of tagOrName) { + const tag = tags[name]; + if (name && tag) { + return tag[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 const IS_PROXY = Symbol('kIsProxy'); const PROXY_SOURCES = Symbol('kProxySources'); @@ -440,42 +429,37 @@ var VueMeta = (function (exports, vue) { sources }; const compute = () => recompute(context); - const addSource = (source, resolveContext, recompute = false) => { - const proxy = createProxy(context, source, resolveContext || {}); - if (recompute) { - compute(); - } - 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); + return { + context, + compute, + addSource: (source, resolveContext, recompute = false) => { + const proxy = createProxy(context, source, resolveContext || {}); if (recompute) { 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 = {}; function renderMeta(context, key, data, config) { // console.info('renderMeta', key, data, config) - if (config.attributesFor) { + if ('attributesFor' in config) { return renderAttributes(context, key, data, config); } - if (config.group) { + if ('group' in config) { return renderGroup(context, key, data, config); } return renderTag(context, key, data, config); @@ -485,7 +469,7 @@ var VueMeta = (function (exports, vue) { if (isArray(data)) { { // 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) return []; @@ -511,7 +495,7 @@ var VueMeta = (function (exports, vue) { function renderTag(context, key, data, config = {}, groupConfig) { // console.info('renderTag', key, data, config, groupConfig) 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)) { return data .map((child) => { @@ -520,7 +504,7 @@ var VueMeta = (function (exports, vue) { .flat(); } const { tag = config.tag || key } = data; - let content; + let content = ''; let hasChilds = false; let isRaw = false; if (isString(data)) { @@ -572,24 +556,24 @@ var VueMeta = (function (exports, vue) { content = getSlotContent(context, slotName, content, data); } else { - const contentAsAttribute = getConfig('contentAsAttribute'); - let valueAttribute = config.valueAttribute; + const contentAsAttribute = !!getTagConfig('contentAsAttribute'); + let { valueAttribute } = config; if (!valueAttribute && contentAsAttribute) { - const tagAttributes = getConfig('attributes'); - valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0]; + const [tagAttribute] = getTagConfig('attributes'); + valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute; } if (!valueAttribute) { content = getSlotContent(context, slotName, content, data); } else { - if (!config.nameless) { - const keyAttribute = getConfig('keyAttribute'); + const { nameless, keyAttribute } = config; + if (!nameless) { if (keyAttribute) { attributes[keyAttribute] = fullName; } } attributes[valueAttribute] = getSlotContent(context, slotName, attributes[valueAttribute] || content, groupConfig); - content = undefined; + content = ''; } } const finalTag = groupConfig && groupConfig.tagNamespace @@ -599,32 +583,32 @@ var VueMeta = (function (exports, vue) { // console.log(' ATTRIBUTES', attributes) // console.log(' CONTENT', content) // // console.log(data, attributes, config) - let vnode; - if (isRaw) { + if (isRaw && 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 { to: data.to, vnode }; } - function renderAttributes(context, key, data, config = {}) { + function renderAttributes(context, key, data, config) { // console.info('renderAttributes', key, data, config) const { attributesFor } = config; + if (!attributesFor) { + return; + } if (!cachedElements[attributesFor]) { const [el, el2] = Array.from(document.querySelectorAll(attributesFor)); if ( !el) { // eslint-disable-next-line no-console - console.error('Could not find element with selector', attributesFor, ', won\'t render attributes'); + console.error('Could not find element for selector', attributesFor, ', won\'t render attributes'); return; } if ( el2) { // eslint-disable-next-line no-console - console.warn('Found multiple elements with selector', attributesFor); + console.warn('Found multiple elements for selector', attributesFor); } cachedElements[attributesFor] = { el, @@ -634,7 +618,7 @@ var VueMeta = (function (exports, vue) { const { el, attrs } = cachedElements[attributesFor]; for (const attr in data) { const content = getSlotContent(context, `${key}(${attr})`, data[attr], data); - el.setAttribute(attr, `${content || ''}`); + el.setAttribute(attr, content || ''); if (!attrs.includes(attr)) { attrs.push(attr); } @@ -645,19 +629,22 @@ var VueMeta = (function (exports, vue) { } } function getSlotContent({ metainfo, slots }, slotName, content, groupConfig) { - if (!slots || !slots[slotName]) { + const slot = slots && slots[slotName]; + if (!slot) { return content; } - const slotProps = { + const slotScopeProps = { content, metainfo }; 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) { - return slotContent[0].children; + const { children } = slotContent[0]; + return children ? children.toString() : ''; } return content; } @@ -668,15 +655,18 @@ var VueMeta = (function (exports, vue) { hasSymbol ? Symbol( '[vue-meta]: ' + name ) : ( '[vue-meta]: ' ) + name; - const metaInfoKey = PolySymbol( 'metainfo' ); + const metaActiveKey = /*#__PURE__*/ PolySymbol( 'active_meta' ); function getCurrentManager(vm) { if (!vm) { - vm = vue.getCurrentInstance(); + vm = vue.getCurrentInstance() || undefined; + } + if (!vm) { + return undefined; } return vm.appContext.config.globalProperties.$metaManager; } - function useMeta(obj, manager) { + function useMeta(source, manager) { const vm = vue.getCurrentInstance(); if (!manager && vm) { manager = getCurrentManager(vm); @@ -685,10 +675,10 @@ var VueMeta = (function (exports, vue) { // oopsydoopsy throw new Error('No manager or current instance'); } - return manager.addMeta(obj, vm || undefined); + return manager.addMeta(source, vm || undefined); } - function useMetainfo() { - return vue.inject(metaInfoKey); + function useActiveMeta() { + return vue.inject(metaActiveKey); } const MetainfoImpl = vue.defineComponent({ @@ -708,22 +698,24 @@ var VueMeta = (function (exports, vue) { const ssrAttribute = 'data-vm-ssr'; const active = vue.reactive({}); - function addVnode(teleports, to, _vnodes) { - const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]); + function addVnode(teleports, to, vnodes) { + const nodes = (isArray(vnodes) ? vnodes : [vnodes]); { // 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) { - vnodes.splice(idx, 1); + nodes.splice(idx, 1); } }); + // only add ssrAttribute's for real meta tags } if (!teleports[to]) { teleports[to] = []; } - teleports[to].push(...vnodes); + teleports[to].push(...nodes); } function createMetaManager(config, resolver) { + let cleanedUpSsr = false; const resolve = (options, contexts, active, key, pathSegments) => { if (isFunction(resolver)) { return resolver(options, contexts, active, key, pathSegments); @@ -731,16 +723,18 @@ var VueMeta = (function (exports, vue) { return resolver.resolve(options, contexts, active, key, pathSegments); }; const { addSource, delSource } = createMergedObject(resolve, active); - let cleanedUpSsr = false; // TODO: validate resolver const manager = { config, install(app) { app.component('Metainfo', Metainfo); app.config.globalProperties.$metaManager = manager; - app.provide(metaInfoKey, active); + app.provide(metaActiveKey, active); }, addMeta(metaObj, vm) { + if (!vm) { + vm = vue.getCurrentInstance() || undefined; + } const resolveContext = { vm }; if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { resolver.setup(resolveContext); @@ -760,9 +754,9 @@ var VueMeta = (function (exports, vue) { // cleanup ssr tags if not yet done if ( !cleanedUpSsr) { cleanedUpSsr = true; - // Listen for DOM loaded because tags in the body couldnt be loaded - // yet once the manager does it first render - // (preferable there should only be one render on hydration) + // Listen for DOM loaded because tags in the body couldnt + // have loaded yet once the manager does it first render + // (preferable there should only be one meta render on hydration) window.addEventListener('DOMContentLoaded', () => { const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`); if (ssrTags && ssrTags.length) { @@ -773,21 +767,34 @@ var VueMeta = (function (exports, vue) { const teleports = {}; for (const key in active) { const config = this.config[key] || {}; - const vnode = renderMeta({ metainfo: active, slots }, key, active[key], config); - if (!vnode) { + let renderedNodes = renderMeta({ metainfo: active, slots }, key, active[key], config); + if (!renderedNodes) { continue; } - const vnodes = isArray(vnode) ? vnode : [vnode]; - const defaultTo = (key !== 'base' && active[key].to) || config.to || (config.attributesFor ? key : 'head'); - for (const { to, vnode } of vnodes) { - addVnode(teleports, to || defaultTo, vnode); + if (!isArray(renderedNodes)) { + renderedNodes = [renderedNodes]; + } + let defaultTo = key !== 'base' && active[key].to; + if (!defaultTo && 'to' in config) { + defaultTo = config.to; + } + if (!defaultTo && 'attributesFor' in config) { + defaultTo = key; + } + for (const { to, vnode } of renderedNodes) { + addVnode(teleports, to || defaultTo || 'head', vnode); } } if (slots) { - for (const tag in slots) { - const slotFn = slots[tag]; - if (isFunction(slotFn)) { - addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })); + for (const slotName in slots) { + const tagName = slotName === 'default' ? 'head' : slotName; + // Only teleport the contents of head/body slots + if (tagName !== 'head' && tagName !== 'body') { + continue; + } + const slot = slots[slotName]; + if (isFunction(slot)) { + addVnode(teleports, tagName, slot({ metainfo: active })); } } } @@ -804,8 +811,8 @@ var VueMeta = (function (exports, vue) { exports.defaultConfig = defaultConfig; exports.getCurrentManager = getCurrentManager; exports.resolveOption = resolveOption; + exports.useActiveMeta = useActiveMeta; exports.useMeta = useMeta; - exports.useMetainfo = useMetainfo; Object.defineProperty(exports, '__esModule', { value: true }); diff --git a/dist/vue-meta.global.min.js b/dist/vue-meta.global.min.js index 316508d..5469948 100644 --- a/dist/vue-meta.global.min.js +++ b/dist/vue-meta.global.min.js @@ -5,4 +5,4 @@ * - All the amazing contributors * @license MIT */ -var VueMeta=function(e,t){"use strict";const r=e=>(t,r)=>{let o=-1;if(r.reduce(((t,r,n)=>{const s=e(t,r);return s!==t?(o=n,s):t}),void 0),o>-1)return t[o]};const o=r(((e,t)=>{const{depth:r}=t;if(!e||r>e)return e}));var n=Object.freeze({__proto__:null,setup:function(e){let t=0;if(e.vm){let{vm:r}=e;do{r.parent&&(t++,r=r.parent)}while(r&&r.parent&&r!==r.root)}e.depth=t},resolve:o});"production"===process.env.NODE_ENV||Object.freeze({}),"production"===process.env.NODE_ENV||Object.freeze([]);const s=Array.isArray,i=e=>"function"==typeof e,c=e=>"string"==typeof e,a=e=>null!==e&&"object"==typeof e,u=Object.prototype.toString,f=e=>"[object Object]"===u.call(e),l={title:{attributes:!1},base:{contentAsAttribute:!0,attributes:["href","target"]},meta:{contentAsAttribute:!0,keyAttribute:"name",attributes:["content","name","http-equiv","charset"]},link:{contentAsAttribute:!0,attributes:["href","crossorigin","rel","media","integrity","hreflang","type","referrerpolicy","sizes","imagesrcset","imagesizes","as","color"]},style:{attributes:["media"]},script:{attributes:["src","type","nomodule","async","defer","crossorigin","integrity","referrerpolicy"]},noscript:{attributes:!1}};const p=Symbol("kIsProxy"),d=Symbol("kProxySources"),m=Symbol("kProxyTarget"),b=Symbol("kResolveContext");function g(e){if(s(e))return e.map(g);if(a(e)){const t={};for(const r in e)t[r]="context"===r?e[r]:g(e[r]);return t}return e}const y=(e,t,r)=>{const o=[];for(const n of e)t in n&&(o.push(n[t]),r&&r(n));return o},h=(e,t,r,o=[])=>{if(o.length||(r||(r=e.active),t||(t=e.sources)),!r||!t)return;const n=((e,...t)=>{const r=e?Object.keys(e):[];if(t)for(const e of t)if(e&&a(e))for(const t in e)r.includes(t)||r.push(t);return r})(...t),i=Object.keys(r);for(const e of i)n.includes(e)||delete r[e];for(const i of n){if(f(t[0][i])){r[i]||(r[i]={});const n=[];for(const e of t)i in e&&n.push(e[i]);h(e,n,r[i],[...o,i]);continue}!r[i]&&s(t[0][i])&&(r[i]=[]);const n=[],c=y(t,i,(e=>n.push(e[b])));let a=e.resolve(c,n,r[i],i,o);f(a)&&(a=g(a)),r[i]=a}},v=(e,r,o,n=[])=>{const s=A(e,o,n),i=t.markRaw(new Proxy(r,s));return!n.length&&e.sources&&e.sources.push(i),i},A=(e,t,r=[])=>({get:(o,n,s)=>{if(n===p)return!0;if(n===d)return e.sources;if(n===m)return o;if(n===b)return t;let i=Reflect.get(o,n,s);if(!a(i))return i;if(!i[p]){const s=[...r,n];i=v(e,i,t,s),o[n]=i}return i},set:(t,o,n)=>{const i=Reflect.set(t,o,n);if(i){const n=s(t);let c,a=!1,{sources:u,active:l}=e,p=0;for(const e of r){if(u=y(u,e),n&&p===r.length-1){c=e;break}s(l)&&(a=!0),l=l[e],p++}if(a)return h(e),i;let d,m=[];n?(d=u,m=u.map((e=>e[b]))):d=y(u,o,(e=>m.push(e[b])));let v=e.resolve(d,m,l,o,r);f(v)&&(v=g(v)),n&&c?l[c]=v:l[o]=v}return i},deleteProperty:(t,o)=>{const n=Reflect.deleteProperty(t,o);if(n){const n=s(t);let i,c=e.sources,a=e.active,u=0;for(const e of r){if(c=c.map((t=>t[e])),n&&u===r.length-1){i=e;break}a=a[e],u++}if(c.some((e=>o in e))){let t,s=[];n?(t=c,s=c.map((e=>e[b]))):t=y(c,o,(e=>s.push(e[b])));let u=e.resolve(t,s,a,o,r);f(u)&&(u=g(u)),n&&i?a[i]=u:a[o]=u}else delete a[o]}return n}}),S={};function k(e,t,r,o){return o.attributesFor?function(e,t,r,o={}){const{attributesFor:n}=o;if(!S[n]){const[e,t]=Array.from(document.querySelectorAll(n));if(!e)return void console.error("Could not find element with selector",n,", won't render attributes");t&&console.warn("Found multiple elements with selector",n),S[n]={el:e,attrs:[]}}const{el:s,attrs:i}=S[n];for(const o in r){const n=w(e,`${t}(${o})`,r[o],r);s.setAttribute(o,`${n||""}`),i.includes(o)||i.push(o)}const c=i.filter((e=>!r[e]));for(const e of c)s.removeAttribute(e)}(e,t,r,o):o.group?function(e,t,r,o){if(s(r))return 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"),[];return Object.keys(r).map((n=>{const s={group:t,data:r};if(o.namespaced)s.tagNamespace=!0===o.namespaced?t:o.namespaced;else if(o.namespacedAttribute){const e=!0===o.namespacedAttribute?t:o.namespacedAttribute;s.fullName=`${e}:${n}`,s.slotName=`${e}(${n})`}return j(e,t,r[n],o,s)})).flat()}(e,t,r,o):j(e,t,r,o)}function j(e,r,o,n={},i){const a=["content","json","rawContent"],u=e=>function(e,t,r){if(r&&t in r)return r[t];if(s(e)){for(const r of e)if(r&&r in l)return l[r][t]}else if(e in l)return l[e][t]}([f,n.tag],e,n);if(s(o))return o.map((t=>j(e,r,t,n,i))).flat();const{tag:f=n.tag||r}=o;let p,d=!1,m=!1;if(c(o))p=o;else if(o.children&&s(o.children))d=!0,p=o.children.map((t=>{const o=j(e,r,t,n,i);return s(o)?o.map((({vnode:e})=>e)):o.vnode}));else{let e=0;for(const t of a){if(!p&&o[t]){p=1===e?JSON.stringify(o[t]):o[t],m=e>1;break}e++}}const b=i&&i.fullName||r,g=i&&i.slotName||r;let{attrs:y}=o;if(y||"object"!=typeof o)y||(y={});else{y={...o},delete y.tag,delete y.children,delete y.to;for(const e of a)delete y[e]}if(d)p=w(e,g,p,o);else{const t=u("contentAsAttribute");let r=n.valueAttribute;if(!r&&t){const e=u("attributes");r=c(t)?t:e[0]}if(r){if(!n.nameless){const e=u("keyAttribute");e&&(y[e]=b)}y[r]=w(e,g,y[r]||p,i),p=void 0}else p=w(e,g,p,o)}const h=i&&i.tagNamespace?`${i.tagNamespace}:${f}`:f;let v;return m?(y.innerHTML=p,v=t.h(h,y)):v=t.h(h,y,p),{to:o.to,vnode:v}}function w({metainfo:e,slots:t},r,o,n){if(!t||!t[r])return o;const s={content:o,metainfo:e};n&&n.group&&(s[n.group]=n.data);const i=t[r](s);return i&&i.length?i[0].children:o}const M="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,N=(O="metainfo",M?Symbol("[vue-meta]: "+O):"[vue-meta]: "+O);var O;function C(e){return e||(e=t.getCurrentInstance()),e.appContext.config.globalProperties.$metaManager}const $=t.defineComponent({name:"Metainfo",inheritAttrs:!1,setup:(e,{slots:t})=>()=>{const e=C();if(e)return e.render({slots:t})}}),x=t.reactive({});function P(e,r,o){const n=s(o)?o:[o];n.forEach(((e,r)=>{e.type===t.Comment&&n.splice(r,1)})),e[r]||(e[r]=[]),e[r].push(...n)}return e.createMetaManager=function(e,r){const{addSource:o,delSource:n}=((e,t={})=>{const r=[];t||(t={});const o={active:t,resolve:e,sources:r},n=()=>h(o);return{context:o,active:t,resolve:e,sources:r,addSource:(e,t,r=!1)=>{const s=v(o,e,t||{});return r&&n(),s},delSource:(e,t=!0)=>{const o=r.findIndex((t=>t===e||t[m]===e));return o>-1&&(r.splice(o,1),t&&n(),!0)},compute:n}})(((e,t,o,n,s)=>i(r)?r(e,t,o,n,s):r.resolve(e,t,o,n,s)),x);let c=!1;const a={config:e,install(e){e.component("Metainfo",$),e.config.globalProperties.$metaManager=a,e.provide(N,x)},addMeta(e,s){const c={vm:s};r&&"setup"in r&&i(r.setup)&&r.setup(c);const a=o(e,c,!0),u=()=>n(a);return s&&t.onUnmounted(u),{meta:a,unmount:u}},render({slots:e}={}){c||(c=!0,window.addEventListener("DOMContentLoaded",(()=>{const e=document.querySelectorAll("[data-vm-ssr]");e&&e.length&&Array.from(e).forEach((e=>e.parentNode&&e.parentNode.removeChild(e)))})));const r={};for(const t in x){const o=this.config[t]||{},n=k({metainfo:x,slots:e},t,x[t],o);if(!n)continue;const i=s(n)?n:[n],c="base"!==t&&x[t].to||o.to||(o.attributesFor?t:"head");for(const{to:e,vnode:t}of i)P(r,e||c,t)}if(e)for(const t in e){const o=e[t];i(o)&&P(r,"default"===t?"head":t,o({metainfo:x}))}return Object.keys(r).map((e=>t.h(t.Teleport,{to:e},r[e])))}};return a},e.deepestResolver=n,e.defaultConfig={body:{tag:"script",to:"body"},base:{valueAttribute:"href"},charset:{tag:"meta",nameless:!0,valueAttribute:"charset"},description:{tag:"meta"},og:{group:!0,namespacedAttribute:!0,tag:"meta",keyAttribute:"property"},twitter:{group:!0,namespacedAttribute:!0,tag:"meta"},htmlAttrs:{attributesFor:"html"},headAttrs:{attributesFor:"head"},bodyAttrs:{attributesFor:"body"}},e.getCurrentManager=C,e.resolveOption=r,e.useMeta=function(e,r){const o=t.getCurrentInstance();if(!r&&o&&(r=C(o)),!r)throw new Error("No manager or current instance");return r.addMeta(e,o||void 0)},e.useMetainfo=function(){return t.inject(N)},Object.defineProperty(e,"__esModule",{value:!0}),e}({},Vue); +var VueMeta=function(t,e){"use strict";const r=t=>(e,r)=>{let n=-1;if(r.reduce(((e,r,o)=>{const s=t(e,r);return s!==e?(n=o,s):e}),void 0),n>-1)return e[n]};const n=r(((t,e)=>{const{depth:r}=e;if(!t||r>t)return t}));var o=Object.freeze({__proto__:null,setup:function(t){let e=0;if(t.vm){let{vm:r}=t;do{r.parent&&(e++,r=r.parent)}while(r&&r.parent&&r!==r.root)}t.depth=e},resolve:n});const s={title:{attributes:!1},base:{contentAsAttribute:!0,attributes:["href","target"]},meta:{contentAsAttribute:!0,keyAttribute:"name",attributes:["content","name","http-equiv","charset"]},link:{contentAsAttribute:!0,attributes:["href","crossorigin","rel","media","integrity","hreflang","type","referrerpolicy","sizes","imagesrcset","imagesizes","as","color"]},style:{attributes:["media"]},script:{attributes:["src","type","nomodule","async","defer","crossorigin","integrity","referrerpolicy"]},noscript:{attributes:!1}};"production"===process.env.NODE_ENV||Object.freeze({}),"production"===process.env.NODE_ENV||Object.freeze([]);const c=Array.isArray,i=t=>"function"==typeof t,a=t=>"string"==typeof t,u=t=>null!==t&&"object"==typeof t,f=Object.prototype.toString,l=t=>"[object Object]"===f.call(t),d=Symbol("kIsProxy"),p=Symbol("kProxySources"),m=Symbol("kProxyTarget"),b=Symbol("kResolveContext");function g(t){if(c(t))return t.map(g);if(u(t)){const e={};for(const r in t)e[r]="context"===r?t[r]:g(t[r]);return e}return t}const y=(t,e,r)=>{const n=[];for(const o of t)e in o&&(n.push(o[e]),r&&r(o));return n},h=(t,e,r,n=[])=>{if(n.length||(r||(r=t.active),e||(e=t.sources)),!r||!e)return;const o=((t,...e)=>{const r=t?Object.keys(t):[];if(e)for(const t of e)if(t&&u(t))for(const e in t)r.includes(e)||r.push(e);return r})(...e),s=Object.keys(r);for(const t of s)o.includes(t)||delete r[t];for(const s of o){if(l(e[0][s])){r[s]||(r[s]={});const o=[];for(const t of e)s in t&&o.push(t[s]);h(t,o,r[s],[...n,s]);continue}!r[s]&&c(e[0][s])&&(r[s]=[]);const o=[],i=y(e,s,(t=>o.push(t[b])));let a=t.resolve(i,o,r[s],s,n);l(a)&&(a=g(a)),r[s]=a}},v=(t,r,n,o=[])=>{const s=A(t,n,o),c=e.markRaw(new Proxy(r,s));return!o.length&&t.sources&&t.sources.push(c),c},A=(t,e,r=[])=>({get:(n,o,s)=>{if(o===d)return!0;if(o===p)return t.sources;if(o===m)return n;if(o===b)return e;let c=Reflect.get(n,o,s);if(!u(c))return c;if(!c[d]){const s=[...r,o];c=v(t,c,e,s),n[o]=c}return c},set:(e,n,o)=>{const s=Reflect.set(e,n,o);if(s){const o=c(e);let i,a=!1,{sources:u,active:f}=t,d=0;for(const t of r){if(u=y(u,t),o&&d===r.length-1){i=t;break}c(f)&&(a=!0),f=f[t],d++}if(a)return h(t),s;let p,m=[];o?(p=u,m=u.map((t=>t[b]))):p=y(u,n,(t=>m.push(t[b])));let v=t.resolve(p,m,f,n,r);l(v)&&(v=g(v)),o&&i?f[i]=v:f[n]=v}return s},deleteProperty:(e,n)=>{const o=Reflect.deleteProperty(e,n);if(o){const o=c(e);let s,i=t.sources,a=t.active,u=0;for(const t of r){if(i=i.map((e=>e[t])),o&&u===r.length-1){s=t;break}a=a[t],u++}if(i.some((t=>n in t))){let e,c=[];o?(e=i,c=i.map((t=>t[b]))):e=y(i,n,(t=>c.push(t[b])));let u=t.resolve(e,c,a,n,r);l(u)&&(u=g(u)),o&&s?a[s]=u:a[n]=u}else delete a[n]}return o}}),S={};function k(t,e,r,n){return"attributesFor"in n?function(t,e,r,n){const{attributesFor:o}=n;if(!o)return;if(!S[o]){const[t,e]=Array.from(document.querySelectorAll(o));if(!t)return void console.error("Could not find element for selector",o,", won't render attributes");e&&console.warn("Found multiple elements for selector",o),S[o]={el:t,attrs:[]}}const{el:s,attrs:c}=S[o];for(const n in r){const o=M(t,`${e}(${n})`,r[n],r);s.setAttribute(n,o||""),c.includes(n)||c.push(n)}const i=c.filter((t=>!r[t]));for(const t of i)s.removeAttribute(t)}(t,e,r,n):"group"in n?function(t,e,r,n){if(c(r))return console.warn("Specifying an array for group properties isnt supported"),[];return Object.keys(r).map((o=>{const s={group:e,data:r};if(n.namespaced)s.tagNamespace=!0===n.namespaced?e:n.namespaced;else if(n.namespacedAttribute){const t=!0===n.namespacedAttribute?e:n.namespacedAttribute;s.fullName=`${t}:${o}`,s.slotName=`${t}(${o})`}return j(t,e,r[o],n,s)})).flat()}(t,e,r,n):j(t,e,r,n)}function j(t,r,n,o={},i){const u=["content","json","rawContent"],f=t=>function(t,e){for(const r of t){const t=s[r];if(r&&t)return t[e]}}([l,o.tag],t);if(c(n))return n.map((e=>j(t,r,e,o,i))).flat();const{tag:l=o.tag||r}=n;let d="",p=!1,m=!1;if(a(n))d=n;else if(n.children&&c(n.children))p=!0,d=n.children.map((e=>{const n=j(t,r,e,o,i);return c(n)?n.map((({vnode:t})=>t)):n.vnode}));else{let t=0;for(const e of u){if(!d&&n[e]){d=1===t?JSON.stringify(n[e]):n[e],m=t>1;break}t++}}const b=i&&i.fullName||r,g=i&&i.slotName||r;let{attrs:y}=n;if(y||"object"!=typeof n)y||(y={});else{y={...n},delete y.tag,delete y.children,delete y.to;for(const t of u)delete y[t]}if(p)d=M(t,g,d,n);else{const e=!!f("contentAsAttribute");let{valueAttribute:r}=o;if(!r&&e){const[t]=f("attributes");r=a(e)?e:t}if(r){const{nameless:e,keyAttribute:n}=o;e||n&&(y[n]=b),y[r]=M(t,g,y[r]||d,i),d=""}else d=M(t,g,d,n)}const h=i&&i.tagNamespace?`${i.tagNamespace}:${l}`:l;m&&d&&(y.innerHTML=d);const v=e.h(h,y,d||void 0);return{to:n.to,vnode:v}}function M({metainfo:t,slots:e},r,n,o){const s=e&&e[r];if(!s)return n;const c={content:n,metainfo:t};if(o&&o.group){const{group:t,data:e}=o;c[t]=e}const i=s(c);if(i&&i.length){const{children:t}=i[0];return t?t.toString():""}return n}const N="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,O=(t=>N?Symbol("[vue-meta]: "+t):"[vue-meta]: "+t)("active_meta");function C(t){if(t||(t=e.getCurrentInstance()||void 0),t)return t.appContext.config.globalProperties.$metaManager}const w=e.defineComponent({name:"Metainfo",inheritAttrs:!1,setup:(t,{slots:e})=>()=>{const t=C();if(t)return t.render({slots:e})}}),$=e.reactive({});function x(t,r,n){const o=c(n)?n:[n];o.forEach(((t,r)=>{t.type===e.Comment&&o.splice(r,1)})),t[r]||(t[r]=[]),t[r].push(...o)}return t.createMetaManager=function(t,r){let n=!1;const{addSource:o,delSource:s}=((t,e={})=>{const r=[];e||(e={});const n={active:e,resolve:t,sources:r},o=()=>h(n);return{context:n,compute:o,addSource:(t,e,r=!1)=>{const s=v(n,t,e||{});return r&&o(),s},delSource:(t,e=!0)=>{const n=r.findIndex((e=>e===t||e[m]===t));return n>-1&&(r.splice(n,1),e&&o(),!0)}}})(((t,e,n,o,s)=>i(r)?r(t,e,n,o,s):r.resolve(t,e,n,o,s)),$),a={config:t,install(t){t.component("Metainfo",w),t.config.globalProperties.$metaManager=a,t.provide(O,$)},addMeta(t,n){n||(n=e.getCurrentInstance()||void 0);const c={vm:n};r&&"setup"in r&&i(r.setup)&&r.setup(c);const a=o(t,c,!0),u=()=>s(a);return n&&e.onUnmounted(u),{meta:a,unmount:u}},render({slots:t}={}){n||(n=!0,window.addEventListener("DOMContentLoaded",(()=>{const t=document.querySelectorAll("[data-vm-ssr]");t&&t.length&&Array.from(t).forEach((t=>t.parentNode&&t.parentNode.removeChild(t)))})));const r={};for(const e in $){const n=this.config[e]||{};let o=k({metainfo:$,slots:t},e,$[e],n);if(!o)continue;c(o)||(o=[o]);let s="base"!==e&&$[e].to;!s&&"to"in n&&(s=n.to),!s&&"attributesFor"in n&&(s=e);for(const{to:t,vnode:e}of o)x(r,t||s||"head",e)}if(t)for(const e in t){const n="default"===e?"head":e;if("head"!==n&&"body"!==n)continue;const o=t[e];i(o)&&x(r,n,o({metainfo:$}))}return Object.keys(r).map((t=>e.h(e.Teleport,{to:t},r[t])))}};return a},t.deepestResolver=o,t.defaultConfig={body:{tag:"script",to:"body"},base:{valueAttribute:"href"},charset:{tag:"meta",nameless:!0,valueAttribute:"charset"},description:{tag:"meta"},og:{group:!0,namespacedAttribute:!0,tag:"meta",keyAttribute:"property"},twitter:{group:!0,namespacedAttribute:!0,tag:"meta"},htmlAttrs:{attributesFor:"html"},headAttrs:{attributesFor:"head"},bodyAttrs:{attributesFor:"body"}},t.getCurrentManager=C,t.resolveOption=r,t.useActiveMeta=function(){return e.inject(O)},t.useMeta=function(t,r){const n=e.getCurrentInstance();if(!r&&n&&(r=C(n)),!r)throw new Error("No manager or current instance");return r.addMeta(t,n||void 0)},Object.defineProperty(t,"__esModule",{value:!0}),t}({},Vue); diff --git a/examples/vue-router/App.js b/examples/vue-router/App.js index 66e4acb..508e2ab 100644 --- a/examples/vue-router/App.js +++ b/examples/vue-router/App.js @@ -1,5 +1,5 @@ import { watch } from 'vue' -import { useMeta, useMetainfo } from 'vue-meta' +import { useMeta, useActiveMeta } from 'vue-meta' export default { setup () { @@ -89,7 +89,7 @@ export default { setTimeout(() => (meta.title = 'My Updated Title'), 2000) setTimeout(() => (meta.htmlAttrs.amp = undefined), 2000) - const metadata = useMetainfo() + const metadata = useActiveMeta() if (!process.server) { window.$metadata = metadata @@ -122,8 +122,7 @@ export default { {{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again - + window.users = [] @@ -149,7 +148,31 @@ gnored in client component templates. --> }, template: ` - + + + + + + window.users = [] + + + + + +
diff --git a/src/Metainfo.ts b/src/Metainfo.ts index df47c34..19cdfca 100644 --- a/src/Metainfo.ts +++ b/src/Metainfo.ts @@ -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 { MetainfoActive } from './types' - -export interface MetainfoProps { - metainfo: MetainfoActive -} export const MetainfoImpl = defineComponent({ name: 'Metainfo', @@ -23,6 +19,8 @@ export const MetainfoImpl = defineComponent({ export const Metainfo = (MetainfoImpl as any) as { new (): { - $props: VNodeProps & MetainfoProps + $props: AllowedComponentProps & + ComponentCustomProps & + VNodeProps } } diff --git a/src/config/default.ts b/src/config/default.ts index 8b1f5e1..656848b 100644 --- a/src/config/default.ts +++ b/src/config/default.ts @@ -1,6 +1,6 @@ -import { Config } from '../types' +import { MetaConfig } from '../types' -export const defaultConfig: Config = { +export const defaultConfig: MetaConfig = { body: { tag: 'script', to: 'body' diff --git a/src/config/helpers.ts b/src/config/helpers.ts index 73ee7ed..d5eaf74 100644 --- a/src/config/helpers.ts +++ b/src/config/helpers.ts @@ -1,32 +1,14 @@ -import { isArray } from '@vue/shared' -import { Config } from '../types' +import type { MetaTagConfigKey, MetaTagName } from '../types' import { tags } from './tags' -export function hasConfig (name: string, config: Config): boolean { - return !!config[name] || !!tags[name] -} - -export function getConfigByKey ( - tagOrName: string | Array, - key: string, - config: Config +export function getTagConfigItem ( + tagOrName: Array, + key: MetaTagConfigKey ): any { - if (config && key in config) { - return config[key] - } - - if (isArray(tagOrName)) { - for (const name of tagOrName) { - if (name && name in tags) { - return tags[name][key] - } + for (const name of tagOrName) { + const tag = tags[name] + if (name && tag) { + return tag[key] } - - return - } - - if (tagOrName in tags) { - const tag = tags[tagOrName] - return tag[key] } } diff --git a/src/config/tags.ts b/src/config/tags.ts index b190bfd..d405e21 100644 --- a/src/config/tags.ts +++ b/src/config/tags.ts @@ -1,17 +1,9 @@ +import type { MetaTagsConfig } from '../types' + /* * This is a fixed config for real HTML tags - * - * TODO: we probably dont need all attributes */ - -export interface TagConfig { - keyAttribute?: string - contentAsAttribute?: boolean | string - attributes: boolean | Array - [key: string]: any -} - -const tags: { [key: string]: TagConfig } = { +export const tags: MetaTagsConfig = { title: { attributes: false }, @@ -61,5 +53,3 @@ const tags: { [key: string]: TagConfig } = { attributes: false } } - -export { tags } diff --git a/src/manager.ts b/src/manager.ts index 417be23..29bc28b 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -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 { createMergedObject } from './object-merge' import { renderMeta } from './render' -import { metaInfoKey } from './symbols' +import { metaActiveKey } from './symbols' import { Metainfo } from './Metainfo' 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 active: MetainfoActive = reactive({}) +export const active: MetaActive = reactive({}) -export function addVnode (teleports: any, to: string, _vnodes: VNode | Array) { - const vnodes = (isArray(_vnodes) ? _vnodes : [_vnodes]) as Array +export function addVnode (teleports: MetaTeleports, to: string, vnodes: VNode | Array): void { + const nodes = (isArray(vnodes) ? vnodes : [vnodes]) as Array - 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 { + if (__BROWSER__) { // 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) { - 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]) { 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) => { if (isFunction(resolver)) { 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) - let cleanedUpSsr = false - // TODO: validate resolver - const manager: Manager = { + const manager: MetaManager = { config, install (app) { app.component('Metainfo', Metainfo) app.config.globalProperties.$metaManager = manager - app.provide(metaInfoKey, active) + app.provide(metaActiveKey, active) }, addMeta (metaObj, vm) { - const resolveContext: MetaContext = { vm } + if (!vm) { + vm = getCurrentInstance() || undefined + } + + const resolveContext: MetaResolveContext = { vm } if (resolver && 'setup' in resolver && isFunction(resolver.setup)) { resolver.setup(resolveContext) } @@ -84,14 +94,14 @@ export function createMetaManager (config: Config, resolver: Resolver | ResolveM } }, - render ({ slots }: any = {}): Array { + render ({ slots } = {}) { // cleanup ssr tags if not yet done if (__BROWSER__ && !cleanedUpSsr) { cleanedUpSsr = true - // Listen for DOM loaded because tags in the body couldnt be loaded - // yet once the manager does it first render - // (preferable there should only be one render on hydration) + // Listen for DOM loaded because tags in the body couldnt + // have loaded yet once the manager does it first render + // (preferable there should only be one meta render on hydration) window.addEventListener('DOMContentLoaded', () => { const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`) @@ -101,36 +111,53 @@ export function createMetaManager (config: Config, resolver: Resolver | ResolveM }) } - const teleports: { [key: string]: VNode | Array } = {} + const teleports: MetaTeleports = {} for (const key in active) { const config = this.config[key] || {} - const vnode = renderMeta( + let renderedNodes = renderMeta( { metainfo: active, slots }, key, active[key], config ) - if (!vnode) { + if (!renderedNodes) { 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) { - addVnode(teleports, to || defaultTo, vnode) + if (!defaultTo && 'to' in config) { + defaultTo = config.to + } + + if (!defaultTo && 'attributesFor' in config) { + defaultTo = key + } + + for (const { to, vnode } of renderedNodes) { + addVnode(teleports, to || defaultTo || 'head', vnode) } } if (slots) { - for (const tag in slots) { - const slotFn = slots[tag] - if (isFunction(slotFn)) { - addVnode(teleports, tag === 'default' ? 'head' : tag, slotFn({ metainfo: active })) + for (const slotName in slots) { + const tagName = slotName === 'default' ? 'head' : slotName + + // Only teleport the contents of head/body slots + if (tagName !== 'head' && tagName !== 'body') { + continue + } + + const slot = slots[slotName] + if (isFunction(slot)) { + addVnode(teleports, tagName, slot({ metainfo: active })) } } } diff --git a/src/object-merge/index.ts b/src/object-merge/index.ts index e9b5c27..4ae6ae7 100644 --- a/src/object-merge/index.ts +++ b/src/object-merge/index.ts @@ -31,7 +31,14 @@ export type MergeContext = { sources: Array } -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 = [] if (!active) { @@ -44,41 +51,34 @@ export const createMergedObject = (resolve: ResolveMethod, active: MergedObject sources } - const compute = () => recompute(context) + const compute: () => void = () => recompute(context) - const addSource = (source: MergeSource, resolveContext: ResolveContext | undefined, recompute: Boolean = false) => { - const proxy = createProxy(context, source, resolveContext || {}) - - if (recompute) { - compute() - } - - 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) + return { + context, + compute, + addSource: (source, resolveContext, recompute = false) => { + const proxy = createProxy(context, source, resolveContext || {}) if (recompute) { 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 } } diff --git a/src/render.ts b/src/render.ts index 9636653..2753a0b 100644 --- a/src/render.ts +++ b/src/render.ts @@ -1,7 +1,19 @@ -import { h, VNode } from 'vue' +import { h } from 'vue' import { isArray, isString } from '@vue/shared' -import { getConfigByKey } from './config' -import { TODO } from './types' +import { getTagConfigItem } from './config' +import type { + MetaConfigSectionAttribute, + MetaConfigSectionGroup, + MetaConfigSectionTag, + MetaConfigSection, + MetaGroupConfig, + MetaRenderContext, + MetaRenderedNode, + MetaRendered, + MetaTagConfigKey, + SlotScopeProperties, + TODO +} from './types' const cachedElements: { [key: string]: { @@ -10,63 +22,37 @@ const cachedElements: { } } = {} -export interface RenderContext { - slots: any - [key: string]: TODO -} - -export interface GroupConfig { - group: string - data: Array | 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 - export function renderMeta ( - context: RenderContext, + context: MetaRenderContext, key: string, data: TODO, - config: TODO -): void | RenderedMetainfo | RenderedMetainfoNode { + config: MetaConfigSection +): void | MetaRendered | MetaRenderedNode { // console.info('renderMeta', key, data, config) - if (config.attributesFor) { - return renderAttributes(context, key, data, config) + if ('attributesFor' in config) { + return renderAttributes(context, key, data, config as MetaConfigSectionAttribute) } - if (config.group) { - return renderGroup(context, key, data, config) + if ('group' in config) { + return renderGroup(context, key, data, config as MetaConfigSectionGroup) } return renderTag(context, key, data, config) } export function renderGroup ( - context: RenderContext, + context: MetaRenderContext, key: string, data: TODO, - config: TODO -): RenderedMetainfo | RenderedMetainfoNode { + config: MetaConfigSectionGroup +): MetaRendered | MetaRenderedNode { // console.info('renderGroup', key, data, config) if (isArray(data)) { if (__DEV__) { // 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) return [] @@ -74,7 +60,7 @@ export function renderGroup ( return Object.keys(data) .map((childKey) => { - const groupConfig: GroupConfig = { + const groupConfig: MetaGroupConfig = { group: key, data } @@ -89,22 +75,22 @@ export function renderGroup ( groupConfig.slotName = `${namespace}(${childKey})` } - return renderTag(context, key, data[childKey], config, groupConfig) + return renderTag(context, key, data[childKey], config as MetaConfigSectionTag, groupConfig) }) .flat() } export function renderTag ( - context: RenderContext, + context: MetaRenderContext, key: string, data: TODO, - config: TODO = {}, - groupConfig?: GroupConfig -): RenderedMetainfo | RenderedMetainfoNode { + config: MetaConfigSectionTag = {}, + groupConfig?: MetaGroupConfig +): MetaRendered | MetaRenderedNode { // console.info('renderTag', key, data, config, groupConfig) 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)) { return data @@ -116,7 +102,7 @@ export function renderTag ( const { tag = config.tag || key } = data - let content + let content: string = '' let hasChilds: boolean = false let isRaw: boolean = false @@ -174,19 +160,19 @@ export function renderTag ( if (hasChilds) { content = getSlotContent(context, slotName, content, data) } else { - const contentAsAttribute = getConfig('contentAsAttribute') - let valueAttribute = config.valueAttribute + const contentAsAttribute = !!getTagConfig('contentAsAttribute') + let { valueAttribute } = config if (!valueAttribute && contentAsAttribute) { - const tagAttributes = getConfig('attributes') - valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttributes[0] + const [tagAttribute] = getTagConfig('attributes') + valueAttribute = isString(contentAsAttribute) ? contentAsAttribute : tagAttribute } if (!valueAttribute) { content = getSlotContent(context, slotName, content, data) } else { - if (!config.nameless) { - const keyAttribute = getConfig('keyAttribute') + const { nameless, keyAttribute } = config + if (!nameless) { if (keyAttribute) { attributes[keyAttribute] = fullName } @@ -199,7 +185,7 @@ export function renderTag ( groupConfig ) - content = undefined + content = '' } } @@ -213,15 +199,13 @@ export function renderTag ( // console.log(' CONTENT', content) // // console.log(data, attributes, config) - let vnode - - if (isRaw) { + if (isRaw && 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 { to: data.to, vnode @@ -229,14 +213,17 @@ export function renderTag ( } export function renderAttributes ( - context: RenderContext, + context: MetaRenderContext, key: string, data: TODO, - config: TODO = {} -): RenderedMetainfoNode | void { + config: MetaConfigSectionAttribute +): MetaRenderedNode | void { // console.info('renderAttributes', key, data, config) const { attributesFor } = config + if (!attributesFor) { + return + } if (!__BROWSER__) { // render attributes in a placeholder vnode so Vue @@ -252,13 +239,13 @@ export function renderAttributes ( if (__DEV__ && !el) { // 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 } if (__DEV__ && el2) { // eslint-disable-next-line no-console - console.warn('Found multiple elements with selector', attributesFor) + console.warn('Found multiple elements for selector', attributesFor) } cachedElements[attributesFor] = { @@ -272,7 +259,7 @@ export function renderAttributes ( for (const attr in data) { const content = getSlotContent(context, `${key}(${attr})`, data[attr], data) - el.setAttribute(attr, `${content || ''}`) + el.setAttribute(attr, content || '') if (!attrs.includes(attr)) { attrs.push(attr) @@ -286,28 +273,31 @@ export function renderAttributes ( } export function getSlotContent ( - { metainfo, slots }: RenderContext, + { metainfo, slots }: MetaRenderContext, slotName: string, - content: any, - groupConfig?: GroupConfig -): TODO { - if (!slots || !slots[slotName]) { + content: string, + groupConfig?: MetaGroupConfig +): string { + const slot = slots && slots[slotName] + if (!slot) { return content } - const slotProps: SlotScopeProperties = { + const slotScopeProps: SlotScopeProperties = { content, metainfo } 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) { - return slotContent[0].children + const { children } = slotContent[0] + return children ? children.toString() : '' } return content diff --git a/src/resolvers/deepest.ts b/src/resolvers/deepest.ts index 8809f14..f73c9ef 100644 --- a/src/resolvers/deepest.ts +++ b/src/resolvers/deepest.ts @@ -1,12 +1,12 @@ import { ResolveContext, ResolveMethod } from '../object-merge' -import { MetaContext } from '../types' +import { MetaResolveContext } from '../types' import { resolveOption } from '.' -type MergeContextDeepest = MetaContext & { +type MergeResolveContextDeepest = MetaResolveContext & { depth: number } -export function setup (context: MergeContextDeepest): void { +export function setup (context: MergeResolveContextDeepest): void { let depth: number = 0 if (context.vm) { @@ -25,7 +25,7 @@ export function setup (context: MergeContextDeepest): void { } 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) { return acc } diff --git a/src/ssr.ts b/src/ssr.ts index 1a3d8a8..94527f3 100644 --- a/src/ssr.ts +++ b/src/ssr.ts @@ -1,7 +1,7 @@ import type { App } from 'vue' 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') export async function renderToStringWithMeta (app: App): Promise<[string, SSRContext]> { diff --git a/src/symbols.ts b/src/symbols.ts index 72ddead..337e3f8 100644 --- a/src/symbols.ts +++ b/src/symbols.ts @@ -1,8 +1,7 @@ import { InjectionKey } from 'vue' -import { MetainfoActive } from './types' +import { MetaActive } from './types' -export const hasSymbol = - typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol' +export const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol' export const PolySymbol = (name: string) => // vm = vue meta @@ -10,6 +9,6 @@ export const PolySymbol = (name: string) => ? Symbol(__DEV__ ? '[vue-meta]: ' + name : name) : (__DEV__ ? '[vue-meta]: ' : '_vm_') + name -export const metaInfoKey = PolySymbol( - __DEV__ ? 'metainfo' : 'mi' -) as InjectionKey +export const metaActiveKey = /*#__PURE__*/ PolySymbol( + __DEV__ ? 'active_meta' : 'am' +) as InjectionKey diff --git a/src/types/config.ts b/src/types/config.ts new file mode 100644 index 0000000..4512786 --- /dev/null +++ b/src/types/config.ts @@ -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 +} + +export type MetaTagName = 'title' | 'base' | 'meta' | 'link' | 'style' | 'script' | 'noscript' + +export type MetaTagsConfig = { + [key in MetaTagName]: MetaTagConfig +} diff --git a/src/types/index.ts b/src/types/index.ts index 33e8be2..e745c83 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -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 { MetaConfig } from './config' + +export * from './config' 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 } -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 } -export interface ConfigOption { - tag?: string - to?: string - group?: boolean - keyAttribute?: string - valueAttribute?: string - nameless?: boolean - namespaced?: boolean - namespacedAttribute?: boolean - attributesFor?: string -} +export type MetaResolveSetup = (context: MetaResolveContext) => void -export interface Config { - [key: string]: ConfigOption -} - -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 +export type MetaResolver = { + setup?: MetaResolveSetup resolve: ResolveMethod } -export type Manager = { - readonly config: Config +/** + * The meta manager + */ +export type MetaManager = { + readonly config: MetaConfig install(app: App): void - addMeta(obj: MetainfoInput, vm?: ComponentInternalInstance): MetaProxy + addMeta(source: MetaSource, vm?: ComponentInternalInstance): MetaProxy - render(ctx: { slots?: any }): Array + render(ctx?: { slots?: Slots }): Array } +/** + * @internal + */ +export type MetaTeleports = { + [key: string]: Array +} + +/** + * @internal + */ +export interface MetaRenderContext { + slots?: Slots + metainfo: MetaActive +} + +/** + * @internal + */ +export interface MetaGroupConfig { + group: string + data: Array | 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 + declare module '@vue/runtime-core' { interface ComponentInternalInstance { - $metaManager: Manager - } -} - -declare global { - namespace NodeJS { - interface Process { - client: boolean - server: boolean - } + $metaManager: MetaManager } } diff --git a/src/useApi.ts b/src/useApi.ts index 4ad4c73..1e98030 100644 --- a/src/useApi.ts +++ b/src/useApi.ts @@ -1,16 +1,20 @@ import { inject, getCurrentInstance, ComponentInternalInstance } from 'vue' -import { metaInfoKey } from './symbols' -import type { Manager, MetainfoActive, MetainfoInput, MetaProxy } from './types' +import { metaActiveKey } from './symbols' +import type { MetaManager, MetaActive, MetaSource, MetaProxy } from './types' -export function getCurrentManager (vm?: ComponentInternalInstance): Manager { +export function getCurrentManager (vm?: ComponentInternalInstance): MetaManager | undefined { if (!vm) { - vm = getCurrentInstance()! + vm = getCurrentInstance() || undefined + } + + if (!vm) { + return undefined } 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() if (!manager && vm) { @@ -22,9 +26,9 @@ export function useMeta (obj: MetainfoInput, manager?: Manager): MetaProxy { throw new Error('No manager or current instance') } - return manager.addMeta(obj, vm || undefined) + return manager.addMeta(source, vm || undefined) } -export function useMetainfo (): MetainfoActive { - return inject(metaInfoKey)! +export function useActiveMeta (): MetaActive { + return inject(metaActiveKey)! }