2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-22 04:50:33 +03:00

test: add render tests

fix: webpack dev server

chore: use eslint not prettier

unfeat: remove support for comments (its brokenn in Vue, maybe later)
This commit is contained in:
pimlie
2020-07-26 00:11:28 +02:00
parent 28d3fc1923
commit 27aaf4744a
80 changed files with 2785 additions and 12109 deletions
+21 -18
View File
@@ -8,12 +8,12 @@ export interface MetainfoProps {
metainfo: MetainfoActive
}
export function addVnode(targets: any, target: string, vnode: VNode) {
if (!targets[target]) {
targets[target] = []
export function addVnode (teleports: any, to: string, vnode: VNode) {
if (!teleports[to]) {
teleports[to] = []
}
targets[target].push(vnode)
teleports[to].push(vnode)
}
export const MetainfoImpl = defineComponent({
@@ -21,12 +21,12 @@ export const MetainfoImpl = defineComponent({
props: {
metainfo: {
type: Object as PropType<MetainfoActive>,
required: true,
},
required: true
}
},
setup({ metainfo }, { slots }) {
setup ({ metainfo }, { slots }) {
return () => {
const targets: any = {}
const teleports: any = {}
const manager = getCurrentManager()
@@ -39,26 +39,29 @@ export const MetainfoImpl = defineComponent({
metainfo[key],
config
)
let defaultTarget =
(key !== 'base' && metainfo[key].target) || config.target || 'head'
console.log('RENDERED VNODES', vnodes)
const defaultTo =
(key !== 'base' && metainfo[key].to) || config.to || 'head'
if (isArray(vnodes)) {
for (const { target, vnode } of vnodes) {
addVnode(targets, target || defaultTarget, vnode)
for (const { to, vnode } of vnodes) {
console.log('VNODE 1', vnode)
addVnode(teleports, to || defaultTo, vnode)
}
continue
}
const { target, vnode } = vnodes
addVnode(targets, target || defaultTarget, vnode)
const { to, vnode } = vnodes
console.log('VNODE 2', vnode)
addVnode(teleports, to || defaultTo, vnode)
}
// console.log('TARGETS', targets)
return Object.keys(targets).map(target => {
return h(Teleport, { to: target }, targets[target])
console.log('TARGETS', teleports)
return Object.keys(teleports).map((to) => {
return h(Teleport, { to }, teleports[to])
})
}
},
}
})
export const Metainfo = (MetainfoImpl as any) as {
+20 -25
View File
@@ -4,10 +4,10 @@ import { TODO } from './types'
export interface ConfigOption {
tag?: string
target?: string
to?: string
group?: boolean
nameAttribute?: string
contentAttribute?: string
keyAttribute?: string
valueAttribute?: string
nameless?: boolean
namespaced?: boolean
namespacedAttribute?: boolean
@@ -16,64 +16,59 @@ export interface ConfigOption {
const defaultMapping: { [key: string]: ConfigOption } = {
body: {
tag: 'script',
target: 'body',
to: 'body'
},
base: {
contentAttribute: 'href',
valueAttribute: 'href'
},
charset: {
tag: 'meta',
nameless: true,
contentAttribute: 'charset',
valueAttribute: 'charset'
},
description: {
tag: 'meta',
tag: 'meta'
},
og: {
group: true,
namespacedAttribute: true,
tag: 'meta',
nameAttribute: 'property',
keyAttribute: 'property'
},
twitter: {
group: true,
namespacedAttribute: true,
tag: 'meta',
},
tag: 'meta'
}
}
export { defaultMapping }
export function hasConfig(name: string): boolean {
export function hasConfig (name: string): boolean {
return !!tags[name] || !!defaultMapping[name]
}
export function getConfigKey(
name: string | Array<string>,
export function getConfigKey (
tagOrName: string | Array<string>,
key: string,
config: TODO,
dontLog?: boolean
config: TODO
): any {
if (!dontLog) {
// console.log('getConfigKey', name, key, getConfigKey(name, key, config, true), config)
}
if (config && key in config) {
return config[key]
}
if (isArray(name)) {
for (const _name of name) {
if (_name && _name in tags) {
return tags[_name][key]
if (isArray(tagOrName)) {
for (const name of tagOrName) {
if (name && name in tags) {
return tags[name][key]
}
}
return
}
if (name in tags) {
const tag = tags[name]
if (tagOrName in tags) {
const tag = tags[tagOrName]
return tag[key]
}
}
+21 -15
View File
@@ -1,22 +1,28 @@
/*
* This is a fixed config for real HTML tags
*
* TODO: we probably dont need all attributes
*/
export interface TagConfig {
nameAttribute?: string
contentAttributes: boolean | Array<string>
keyAttribute?: string
attributes: boolean | Array<string>
[key: string]: any
}
const tags: { [key: string]: TagConfig } = {
title: {
contentAttributes: false,
attributes: false
},
base: {
contentAttributes: ['href', 'target'],
attributes: ['href', 'target']
},
meta: {
nameAttribute: 'name',
contentAttributes: ['content', 'name', 'http-equiv', 'charset'],
keyAttribute: 'name',
attributes: ['content', 'name', 'http-equiv', 'charset']
},
link: {
contentAttributes: [
attributes: [
'href',
'crossorigin',
'rel',
@@ -29,14 +35,14 @@ const tags: { [key: string]: TagConfig } = {
'imagesrcset',
'imagesizes',
'as',
'color',
],
'color'
]
},
style: {
contentAttributes: ['media'],
attributes: ['media']
},
script: {
contentAttributes: [
attributes: [
'src',
'type',
'nomodule',
@@ -44,12 +50,12 @@ const tags: { [key: string]: TagConfig } = {
'defer',
'crossorigin',
'integrity',
'referrerpolicy',
],
'referrerpolicy'
]
},
noscript: {
contentAttributes: false,
},
attributes: false
}
}
export { tags }
+1 -1
View File
@@ -1,2 +1,2 @@
// Global compile-time constants
declare var __DEV__: boolean
declare let __DEV__: boolean
+2 -2
View File
@@ -1,6 +1,6 @@
import { setByObject } from './set'
import { MetaContext } from '../types'
import { setByObject } from './set'
export function remove(context: MetaContext) {
export function remove (context: MetaContext) {
setByObject(context, {})
}
+1 -1
View File
@@ -2,7 +2,7 @@ import { hasOwn } from '@vue/shared'
import { clone } from '../utils'
import { ActiveNode, MetaContext, PathSegments, ShadowNode } from '../types'
export function resolveActive(
export function resolveActive (
context: MetaContext,
key: string,
pathSegments: PathSegments,
+6 -6
View File
@@ -1,9 +1,9 @@
import { isPlainObject, hasOwn } from '@vue/shared'
import { ActiveNode, MetaContext, PathSegments, ShadowNode } from '../types'
import { shadow, active } from './globals'
import { resolveActive } from './resolve'
import { ActiveNode, MetaContext, PathSegments, ShadowNode } from '../types'
export function set(
export function set (
context: MetaContext,
key: string,
value: any,
@@ -54,7 +54,7 @@ export function set(
resolveActive(context, key, pathSegments, shadowParent, activeParent)
}
export function setByObject(
export function setByObject (
context: MetaContext,
value: any,
shadowParent: ShadowNode = shadow,
@@ -70,14 +70,14 @@ export function setByObject(
if (isPlainObject(shadowParent[key])) {
setByObject(context, {}, shadowParent[key], activeParent[key], [
...pathSegments,
key,
key
])
continue
}
set(context, key, undefined, shadowParent, activeParent, [
...pathSegments,
key,
key
])
}
@@ -85,7 +85,7 @@ export function setByObject(
for (const key in value) {
set(context, key, value[key], shadowParent, activeParent, [
...pathSegments,
key,
key
])
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
import { MetaContext, PathSegments, ShadowNode, ActiveNode } from '../types'
import { shadow, active } from './globals'
import { set } from './set'
import { MetaContext, PathSegments, ShadowNode, ActiveNode } from '../types'
export function update(
export function update (
context: MetaContext,
pathSegments: PathSegments,
key: string,
+1 -1
View File
@@ -10,7 +10,7 @@ declare module '@vue/runtime-core' {
}
}
export function applyMetaPlugin(app: App, manager: Manager) {
export function applyMetaPlugin (app: App, manager: Manager) {
app.component('Metainfo', Metainfo)
app.config.globalProperties.$metaManager = manager
+7 -7
View File
@@ -16,21 +16,21 @@ export type Manager = {
install(app: App): void
}
export function createManager(options: TODO = {}): Manager {
export function createManager (options: TODO = {}): Manager {
const { resolver = deepestResolver } = options
// TODO: validate resolver
const manager: Manager = {
resolver: {
setup(context: MetaContext) {
setup (context: MetaContext) {
if (!resolver || isFunction(resolver)) {
return
}
resolver.setup(context)
},
resolve(key: string, pathSegments: PathSegments, getShadow, getActive) {
resolve (key: string, pathSegments: PathSegments, getShadow, getActive) {
if (!resolver) {
return
}
@@ -40,15 +40,15 @@ export function createManager(options: TODO = {}): Manager {
}
return resolver.resolve(key, pathSegments, getShadow, getActive)
},
}
},
config: {
...defaultMapping,
...options.config,
...options.config
},
install(app: App) {
install (app: App) {
applyMetaPlugin(app, this)
},
}
}
return manager
+9 -10
View File
@@ -4,22 +4,22 @@ import { update } from './info/update'
import { MetaContext, MetainfoInput, PathSegments } from './types'
interface Target extends MetainfoInput {
__vm_proxy?: any
__vm_proxy?: any // eslint-disable-line camelcase
}
export function createProxy(
export function createProxy (
target: Target,
handler: ProxyHandler<object>
): Target {
return markRaw(new Proxy(target, handler))
}
export function createHandler(
export function createHandler (
context: MetaContext,
pathSegments: PathSegments = []
): ProxyHandler<object> {
return {
get(target: object, key: string, receiver: object) {
get (target: object, key: string, receiver: object) {
const value = Reflect.get(target, key, receiver)
if (!isObject(value)) {
@@ -29,20 +29,19 @@ export function createHandler(
if (!value.__vm_proxy) {
const keyPath: PathSegments = [...pathSegments, key]
const handler = /*#__PURE__*/ createHandler(context, keyPath)
const handler = /* #__PURE__ */ createHandler(context, keyPath)
value.__vm_proxy = createProxy(value, handler)
}
return value.__vm_proxy
},
set(
target: object,
set (
target: object, // eslint-disable-line @typescript-eslint/no-unused-vars
key: string,
value: unknown,
receiver: object
value: unknown
): boolean {
update(context, pathSegments, key, value)
return true
},
}
}
}
+52 -52
View File
@@ -24,18 +24,18 @@ export interface SlotScopeProperties {
export type RenderedMetainfoNode = {
vnode: VNode
target?: string
to?: string
}
export type RenderedMetainfo = Array<RenderedMetainfoNode>
export function renderMeta(
export function renderMeta (
context: RenderContext,
key: string,
data: TODO,
config: TODO
): RenderedMetainfo | RenderedMetainfoNode {
// console.info('renderMeta', key, data, config)
console.info('renderMeta', key, data, config)
if (config.group) {
return renderGroup(context, key, data, config)
@@ -44,33 +44,32 @@ export function renderMeta(
return renderTag(context, key, data, config)
}
export function renderGroup(
export function renderGroup (
context: RenderContext,
key: string,
data: TODO,
config: TODO
): RenderedMetainfo | RenderedMetainfoNode {
// console.info('renderGroup', key, data, config)
console.info('renderGroup', key, data, config)
if (isArray(data)) {
config.contentAttributes = getConfigKey(
[key, config.tag],
'contentAttributes',
config
)
return data.map(_data => renderTag(context, key, _data, config)).flat()
if (__DEV__) {
// eslint-disable-next-line no-console
console.warn('Specifying an array for group properties isnt supported as we didnt found a use-case for this yet. If you have one, please create an issue on the vue-meta repo')
}
// config.attributes = getConfigKey([key, config.tag], 'attributes', config)
return []
}
return Object.keys(data)
.map(childKey => {
.map((childKey) => {
const groupConfig: GroupConfig = {
group: key,
data,
data
}
if (config.namespaced) {
groupConfig.tagNamespace =
config.namespaced === true ? key : config.namespaced
groupConfig.tagNamespace = config.namespaced === true ? key : config.namespaced
} else if (config.namespacedAttribute) {
const namespace =
config.namespacedAttribute === true ? key : config.namespacedAttribute
@@ -84,31 +83,37 @@ export function renderGroup(
.flat()
}
export function renderTag(
export function renderTag (
context: RenderContext,
key: string,
data: TODO,
config: TODO = {},
groupConfig?: GroupConfig
): RenderedMetainfo | RenderedMetainfoNode {
console.info('renderTag', key, data, config, groupConfig)
/* TODO: not needed I think
if (!config.group && isArray(data)) {
data = { content: data }
}
data = { children: data }
} */
let content, hasChilds
if (isArray(data)) {
return data
.map(child => {
.map((child) => {
return renderTag(context, key, child, config, groupConfig)
})
.flat()
} else if (data.content && isArray(data.content)) {
content = data.content.map((child: string | TODO) => {
if (typeof child === 'string') {
return child
} else if (data.children && isArray(data.children)) {
content = data.children.map((child: string | TODO) => {
const data = renderTag(context, key, child, config, groupConfig)
if (isArray(data)) {
return data.map(({ vnode }) => vnode)
}
return renderTag(context, key, child, config, groupConfig)
return data.vnode
})
hasChilds = true
} else {
@@ -125,36 +130,35 @@ export function renderTag(
attributes = { ...data }
delete attributes.tag
delete attributes.content
delete attributes.target
delete attributes.children
delete attributes.to
} else {
attributes = {}
}
if (hasChilds) {
content = getSlotContent(context, slotName, content, config, data)
content = getSlotContent(context, slotName, content, data)
} else {
const contentAttributes = getConfigKey(tag, 'contentAttributes', config)
if (contentAttributes) {
const tagAttributes = getConfigKey([tag, config.tag], 'attributes', config)
console.log('tagAttributes', tagAttributes, config, tag)
if (tagAttributes) {
if (!config.nameless) {
const nameAttribute = getConfigKey(tag, 'nameAttribute', config)
if (nameAttribute) {
attributes[nameAttribute] = fullName
const keyAttribute = getConfigKey([tag, config.tag], 'keyAttribute', config)
if (keyAttribute) {
attributes[keyAttribute] = fullName
}
}
const contentAttribute = config.contentAttribute || contentAttributes[0]
attributes[contentAttribute] = getSlotContent(
const valueAttribute = config.valueAttribute || tagAttributes[0]
attributes[valueAttribute] = getSlotContent(
context,
slotName,
attributes[contentAttribute] || content,
config,
attributes[valueAttribute] || content,
groupConfig
)
content = undefined
} else {
content = getSlotContent(context, slotName, content, config, data)
content = getSlotContent(context, slotName, content, data)
}
}
@@ -170,11 +174,8 @@ export function renderTag(
if (hasChilds) {
for (const child of content) {
if (typeof child === 'string') {
continue
}
if (child.type === finalTag) {
// TODO: what was this about again?!?!?!?!
return content
}
@@ -185,36 +186,35 @@ export function renderTag(
const vnode = h(finalTag, attributes, content)
return {
target: data.target,
vnode,
to: data.to,
vnode
}
}
export function getSlotContent(
export function getSlotContent (
{ metainfo, slots }: RenderContext,
slotName: string,
content: any,
config: TODO,
groupConfig?: GroupConfig
): TODO {
if (!slots[slotName]) {
if (!slots || !slots[slotName]) {
return content
}
const slotProps: SlotScopeProperties = {
content,
metainfo,
metainfo
}
if (groupConfig && groupConfig.group) {
slotProps[groupConfig.group] = groupConfig.data
}
content = slots[slotName](slotProps)
const slotContent = slots[slotName](slotProps)
if (content.length) {
return content[0].children
if (slotContent && slotContent.length) {
return slotContent[0].children
}
return ''
return content
}
+4 -4
View File
@@ -1,13 +1,13 @@
import {
ActiveNode,
/*ActiveResolverSetup, ActiveResolverMethod,*/ MetaContext,
/* ActiveResolverSetup, ActiveResolverMethod, */ MetaContext,
PathSegments,
ShadowNode,
ShadowNode
} from '../types'
export function setup(context: MetaContext): void {}
export function setup (context: MetaContext): void {}
export function resolve(
export function resolve (
key: string,
pathSegments: PathSegments,
shadow: ShadowNode,
+7 -8
View File
@@ -7,7 +7,7 @@ import { MetaContext, MetainfoActive, MetainfoInput } from './types'
let contextCounter: number = 0
export function useMeta(obj: MetainfoInput, manager?: Manager) {
export function useMeta (obj: MetainfoInput, manager?: Manager) {
const vm = getCurrentInstance()
if (vm) {
console.log(vm)
@@ -17,16 +17,15 @@ export function useMeta(obj: MetainfoInput, manager?: Manager) {
if (!manager) {
// oopsydoopsy
throw new Error('No manager or current instance')
return
}
const context: MetaContext = {
id: PolySymbol(`context ${contextCounter++}`),
vm,
manager,
manager
}
let unmount = <T extends Function = () => any>() => remove(context)
const unmount = <T extends Function = () => any>() => remove(context)
if (vm) {
onUnmounted(unmount)
}
@@ -37,20 +36,20 @@ export function useMeta(obj: MetainfoInput, manager?: Manager) {
setByObject(context, obj)
const handler = /*#__PURE__*/ createHandler(context)
const handler = /* #__PURE__ */ createHandler(context)
const meta = createProxy(obj, handler)
return {
meta,
unmount,
unmount
}
}
export function useMetainfo(): MetainfoActive {
export function useMetainfo (): MetainfoActive {
return inject(metaInfoKey)!
}
export function getCurrentManager(): Manager {
export function getCurrentManager (): Manager {
const vm = getCurrentInstance()!
return vm.appContext.config.globalProperties.$metaManager
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { isArray, isObject } from '@vue/shared'
// See: https://github.com/vuejs/vue-next/blob/08b4e8815da4e8911058ccbab986bea6365c3352/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
export function clone(v: any): any {
export function clone (v: any): any {
if (isArray(v)) {
return v.map(clone)
}