From 0745f1668388c85d9a4eb50a26a350f8278b490c Mon Sep 17 00:00:00 2001 From: pimlie Date: Sun, 6 Jun 2021 17:19:25 +0200 Subject: [PATCH] feat: add plugin for options api support --- dist/vue-meta-ssr.d.ts | 6 - examples/vue-router/App.ts | 1 + examples/vue-router/Options.ts | 6 + examples/vue-router/{Child.ts => Page.ts} | 5 + examples/vue-router/main.ts | 15 ++- examples/vue-shims.d.ts | 5 + examples/webpack.config.js | 8 +- package.json | 2 +- src/index.ts | 2 +- src/plugin.ts | 35 ++++++ src/types/index.ts | 5 + src/types/options.ts | 137 ++++++++++++++++++++++ tsconfig.json | 5 +- yarn.lock | 2 +- 14 files changed, 213 insertions(+), 21 deletions(-) delete mode 100644 dist/vue-meta-ssr.d.ts create mode 100644 examples/vue-router/Options.ts rename examples/vue-router/{Child.ts => Page.ts} (96%) create mode 100644 examples/vue-shims.d.ts create mode 100644 src/plugin.ts create mode 100644 src/types/options.ts diff --git a/dist/vue-meta-ssr.d.ts b/dist/vue-meta-ssr.d.ts deleted file mode 100644 index 70d53b7b..00000000 --- a/dist/vue-meta-ssr.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { App } from 'vue'; -import { SSRContext } from '@vue/server-renderer'; - -declare function renderMetaToString(app: App, ctx?: SSRContext): Promise; - -export { renderMetaToString }; diff --git a/examples/vue-router/App.ts b/examples/vue-router/App.ts index 9b8ad21d..93df954b 100644 --- a/examples/vue-router/App.ts +++ b/examples/vue-router/App.ts @@ -182,6 +182,7 @@ export default { diff --git a/examples/vue-router/Options.ts b/examples/vue-router/Options.ts new file mode 100644 index 00000000..cff295f1 --- /dev/null +++ b/examples/vue-router/Options.ts @@ -0,0 +1,6 @@ +export default { + metaInfo: { + title: 'Title from Options API' + }, + template: '
This component uses the Options API
' +} diff --git a/examples/vue-router/Child.ts b/examples/vue-router/Page.ts similarity index 96% rename from examples/vue-router/Child.ts rename to examples/vue-router/Page.ts index 2744ef2f..eab42f9b 100644 --- a/examples/vue-router/Child.ts +++ b/examples/vue-router/Page.ts @@ -7,6 +7,11 @@ let metaUpdated = 'no' export default defineComponent({ name: 'ChildComponent', + metaInfo () { + return { + title: 'asdf' + } + }, setup () { const route = useRoute() diff --git a/examples/vue-router/main.ts b/examples/vue-router/main.ts index 763681af..4692ecab 100644 --- a/examples/vue-router/main.ts +++ b/examples/vue-router/main.ts @@ -1,13 +1,14 @@ import { h, createApp as createVueApp, createSSRApp } from 'vue' import { createRouter as createVueRouter, createMemoryHistory, createWebHistory } from 'vue-router' -import { createMetaManager as createVueMetaManager, defaultConfig, useMeta } from '../../src' +import { createMetaManager as createVueMetaManager, defaultConfig, useMeta, plugin } from '../../src' import * as deepestResolver from '../../src/resolvers/deepest' import App from './App' -import ChildComponent from './Child' +import PageComponent from './Page' +import PageOptions from './Options' -function createComponent () { +function createPage () { return { - render: () => h(ChildComponent) + render: () => h(PageComponent) } } /* @@ -34,8 +35,9 @@ const createMetaManager = (isSSR = false) => createVueMetaManager( const createRouter = (base: string, isSSR = false) => createVueRouter({ history: isSSR ? createMemoryHistory(base) : createWebHistory(base), routes: [ - { name: 'home', path: '/', component: createComponent() }, - { name: 'about', path: '/about', component: createComponent() } + { name: 'home', path: '/', component: createPage() }, + { name: 'about', path: '/about', component: createPage() }, + { name: 'options', path: '/options', component: PageOptions } ] }) @@ -46,6 +48,7 @@ const createApp = (base: string, isSSR = null) => { app.use(router) app.use(metaManager) + app.use(plugin) useMeta( { diff --git a/examples/vue-shims.d.ts b/examples/vue-shims.d.ts new file mode 100644 index 00000000..bf92ed96 --- /dev/null +++ b/examples/vue-shims.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import { ComponentOptions } from 'vue' + const component: ComponentOptions + export default component +} diff --git a/examples/webpack.config.js b/examples/webpack.config.js index 32048843..37bb99f2 100644 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -43,14 +43,14 @@ module.exports = (isBrowser) => { }, module: { rules: [ + { + test: /\.vue$/, + use: 'vue-loader' + }, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ - }, - { - test: /\.vue$/, - use: 'vue-loader' } ] }, diff --git a/package.json b/package.json index 9d5f8215..134faab8 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@types/webpack-env": "^1.16.0", "@typescript-eslint/eslint-plugin": "^4.15.2", "@typescript-eslint/parser": "^4.15.2", - "@vue/compiler-sfc": "^3.0.6", + "@vue/compiler-sfc": "^3.0.11", "@vue/server-renderer": "^3.0.6", "@vue/test-utils": "^2.0.0-rc.6", "@wishy-gift/html-include-chunks-webpack-plugin": "^0.1.5", diff --git a/src/index.ts b/src/index.ts index a4ed166a..a29c081f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,8 @@ import * as deepestResolver from './resolvers/deepest' export { defaultConfig } from './config' export { createMetaManager } from './manager' +export { install as plugin } from './plugin' export { resolveOption } from './resolvers' - export * from './types' export * from './useApi' diff --git a/src/plugin.ts b/src/plugin.ts new file mode 100644 index 00000000..c402c3ab --- /dev/null +++ b/src/plugin.ts @@ -0,0 +1,35 @@ +import { isFunction } from '@vue/shared' +import { App, ComponentOptionsMixin, computed, getCurrentInstance } from 'vue' +import { ComponentOptionsMetaInfo } from './types/options' +import { useMeta } from './useApi' + +export type PluginOptions = { + keyName: string +} + +export const defaultOptions: PluginOptions = { + keyName: 'metaInfo' +} + +type CreateMixin = (options: PluginOptions) => ComponentOptionsMixin +export const createMixin: CreateMixin = options => ({ + created () { + const instance = getCurrentInstance() + if (!instance?.type || !(options.keyName in instance.type)) { + return + } + + const metaInfo = (instance.type as any)[options.keyName] as ComponentOptionsMetaInfo + if (isFunction(metaInfo)) { + const computedMetaInfo = computed(metaInfo) + useMeta(computedMetaInfo) + } else { + useMeta(metaInfo) + } + } +}) + +export const install = (app: App, _options: Partial = {}) => { + const options = Object.assign({}, defaultOptions, _options) + app.mixin(createMixin(options)) +} diff --git a/src/types/index.ts b/src/types/index.ts index 26158341..2606b9b1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,4 @@ +import { ComponentOptionsMetaInfo } from './options' import type { VNode, Slots, ComponentInternalInstance } from 'vue' import type { MergedObject, ResolveContext, ResolveMethod } from '../object-merge' import type { MetaManager } from '../manager' @@ -118,4 +119,8 @@ declare module '@vue/runtime-core' { $metaManager: MetaManager $metaGuards: MetaGuards } + + interface ComponentCustomOptions { + metaInfo?: ComponentOptionsMetaInfo + } } diff --git a/src/types/options.ts b/src/types/options.ts new file mode 100644 index 00000000..fd016c0c --- /dev/null +++ b/src/types/options.ts @@ -0,0 +1,137 @@ +export interface AttributeProperty { + [key: string]: string | string[] + } + +export interface MetaDataProperty { + vmid?: string, + once?: boolean, + skip?: boolean, + body?: boolean, + pbody?: boolean, + [key: string]: any + } + +export interface MetaPropertyCharset extends MetaDataProperty { + charset: string, + } + +export interface MetaPropertyEquiv extends MetaDataProperty { + httpEquiv: string, + content: string, + } + +export interface MetaPropertyTrueEquiv extends MetaDataProperty { + 'http-equiv': string, + content: string, + } + +export interface MetaPropertyName extends MetaDataProperty { + name: string, + content: string, + } + +export interface MetaPropertyMicrodata extends MetaDataProperty { + itemprop: string, + content: string, + } + +// non-w3c interface +export interface MetaPropertyProperty extends MetaDataProperty { + property: string, + content: string, + } + +export interface LinkPropertyBase extends MetaDataProperty { + rel: string, + crossOrigin?: string | null, + media?: string, + nonce?: string, + referrerPolicy?: string, + rev?: string, + type?: string + } + +export interface LinkPropertyHref extends LinkPropertyBase { + href?: string, + hreflang?: string, + callback?: void + } + +export interface LinkPropertyHrefCallback extends LinkPropertyBase { + vmid: string, + // callback: CallbackFn, + href?: string, + hreflang?: string + } + +export interface StyleProperty extends MetaDataProperty { + cssText: string, + // callback?: CallbackFn + media?: string, + nonce?: string, + type?: string, + } + +export interface ScriptPropertyBase extends MetaDataProperty { + type?: string, + charset?: string, + async?: boolean, + defer?: boolean, + crossOrigin?: string, + nonce?: string + } + +export interface ScriptPropertyText extends ScriptPropertyBase { + innerHTML: string + } + +export interface ScriptPropertySrc extends ScriptPropertyBase { + src: string, + callback?: void + } + +export interface ScriptPropertySrcCallback extends ScriptPropertyBase { + vmid: string, + // callback: CallbackFn + } + + // eslint-disable-next-line no-use-before-define + type JsonVal = string | number | boolean | JsonObj | JsonObj[] | null + + interface JsonObj { + [key: string]: JsonVal | JsonVal[] + } + +export interface ScriptPropertyJson extends ScriptPropertyBase { + json: JsonObj + } + +export interface NoScriptProperty extends MetaDataProperty { + innerHTML: string, + } + +export interface ComponentMetaInfo { + title?: string + + htmlAttrs?: AttributeProperty + headAttrs?: AttributeProperty + bodyAttrs?: AttributeProperty + + base?: { + target: string, + href: string + } + + meta?: (MetaPropertyCharset | MetaPropertyEquiv | MetaPropertyTrueEquiv | MetaPropertyName | MetaPropertyMicrodata | MetaPropertyProperty)[] + link?: (LinkPropertyBase | LinkPropertyHref | LinkPropertyHrefCallback)[] + style?: StyleProperty[] + script?: (ScriptPropertyText | ScriptPropertySrc | ScriptPropertySrcCallback | ScriptPropertyJson)[] + noscript?: NoScriptProperty[] + + __dangerouslyDisableSanitizers?: string[] + __dangerouslyDisableSanitizersByTagID?: { + [key: string]: string[] + } + } + +export type ComponentOptionsMetaInfo = ComponentMetaInfo | (() => ComponentMetaInfo) diff --git a/tsconfig.json b/tsconfig.json index 6fd7bc31..2cee4706 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,8 +32,9 @@ } }, "include": [ - "src/global.d.ts", "src/**/*.ts", - "test/**/*.ts" + "test/**/*.ts", + "examples/**/*.ts", + "examples/**/*.vue" ], } diff --git a/yarn.lock b/yarn.lock index 8530f506..676bebae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1020,7 +1020,7 @@ "@vue/compiler-core" "3.0.11" "@vue/shared" "3.0.11" -"@vue/compiler-sfc@^3.0.6": +"@vue/compiler-sfc@^3.0.11": version "3.0.11" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.11.tgz#cd8ca2154b88967b521f5ad3b10f5f8b6b665679" integrity sha512-7fNiZuCecRleiyVGUWNa6pn8fB2fnuJU+3AGjbjl7r1P5wBivfl02H4pG+2aJP5gh2u+0wXov1W38tfWOphsXw==