diff --git a/packages/shared/src/meta.ts b/packages/shared/src/meta.ts index c437ab99..392b4041 100644 --- a/packages/shared/src/meta.ts +++ b/packages/shared/src/meta.ts @@ -1,11 +1,9 @@ import { packArray, unpackToArray, unpackToString } from 'packrup' import type { TransformValueOptions } from 'packrup' -import type { Head, MetaFlatInput } from '@unhead/schema' - -export type ValidMetaType = 'name' | 'http-equiv' | 'property' | 'charset' +import type { BaseMeta, Head, MetaFlatInput } from '@unhead/schema' interface PackingDefinition { - metaKey?: ValidMetaType + metaKey?: keyof BaseMeta keyValue?: string unpack?: TransformValueOptions } @@ -143,7 +141,7 @@ const MetaPackingSchema: Record = { }, } as const -export function resolveMetaKeyType(key: string): string { +export function resolveMetaKeyType(key: string): keyof BaseMeta { return MetaPackingSchema[key]?.metaKey || 'name' } @@ -195,14 +193,15 @@ export function resolvePackedMetaObjectValue(value: string, key: string): string const SimpleArrayUnpackMetas: (keyof MetaFlatInput)[] = ['themeColor'] -function getMeta(key: string, value: string) { - const meta: Record = {} +function getMeta(key: string, value?: string) { + const meta: BaseMeta = {} const metaKeyType = resolveMetaKeyType(key) if (metaKeyType === 'charset') { meta[metaKeyType] = value } else { + // @ts-expect-error not sure meta[metaKeyType] = resolveMetaKeyValue(key) meta.content = value } @@ -210,15 +209,22 @@ function getMeta(key: string, value: string) { return meta } -function flattenMetaObjects(input: Record, prefix: string = '') { - const extras: Record[] = [] - - for (const [key, value] of Object.entries(input)) { +function flattenMetaObjects(input: MetaFlatInput, prefix: string = '') { + const extras: BaseMeta[] = [] + for (const [k, v] of Object.entries(input)) { + const key = k as keyof MetaFlatInput + const value = v as MetaFlatInput const fullkey = `${prefix}${prefix === '' ? key : key.charAt(0).toUpperCase() + key.slice(1)}` const unpacker = MetaPackingSchema[key]?.unpack if (unpacker) { - extras.push(getMeta(fullkey, unpackToString(value, unpacker))) + extras.push(getMeta(fullkey, unpackToString(value as Record, unpacker))) + delete input[key] + continue + } + + if (!value) { + extras.push(getMeta(fullkey, value)) delete input[key] continue } @@ -227,7 +233,9 @@ function flattenMetaObjects(input: Record, prefix: string = '') { const children = Array.isArray(value) ? value : [value] for (const child of children) { - if (typeof child === 'object') + if (!child) + extras.push(getMeta(fullkey, child)) + else if (typeof child === 'object') extras.push(...flattenMetaObjects(child, fullkey)) else extras.push(getMeta(fullkey, child)) @@ -249,8 +257,8 @@ function flattenMetaObjects(input: Record, prefix: string = '') { * Converts a flat meta object into an array of meta entries. * @param input */ -export function unpackMeta(input: T): Required['meta'] { - const extras: Record[] = [] +export function unpackMeta(input: T): Required['meta'] { + const extras: BaseMeta[] = [] SimpleArrayUnpackMetas.forEach((meta: keyof T) => { if (input[meta] && typeof input[meta] !== 'string') { @@ -302,9 +310,9 @@ export function unpackMeta(input: T): Required['m return typeof value === 'number' ? value.toString() : value }, - }) + }) as BaseMeta[] // remove keys with defined but empty content - return [...extras, ...meta].filter(v => typeof v.content === 'undefined' || v.content !== '_null') + return [...extras, ...meta] as unknown as Required['meta'] } /** diff --git a/packages/unhead/src/plugins/dedupe.ts b/packages/unhead/src/plugins/dedupe.ts index 33435497..23d6412d 100644 --- a/packages/unhead/src/plugins/dedupe.ts +++ b/packages/unhead/src/plugins/dedupe.ts @@ -87,6 +87,13 @@ export default defineHeadPlugin({ newTags.push(...dupes) }) ctx.tags = newTags + // now filter out invalid meta + // TODO separate plugin + ctx.tags = ctx.tags.filter((t) => { + if (t.tag === 'meta' && t.props.name && !t.props.content) + return false + return true + }) }, }, }) diff --git a/test/unhead/useSeoMeta.test.ts b/test/unhead/useSeoMeta.test.ts index 20a82967..7ce30fe2 100644 --- a/test/unhead/useSeoMeta.test.ts +++ b/test/unhead/useSeoMeta.test.ts @@ -81,9 +81,27 @@ describe('useSeoMeta', () => { `) }) - it('twitter image', async () => { + it('removing with null', async () => { const head = createHead() + useSeoMeta({ + description: 'test', + }) + + expect(await renderSSRHead(head)).toMatchInlineSnapshot(` + { + "bodyAttrs": "", + "bodyTags": "", + "bodyTagsOpen": "", + "headTags": "", + "htmlAttrs": "", + } + `) + + useSeoMeta({ + description: null, + }) + expect(await renderSSRHead(head)).toMatchInlineSnapshot(` { "bodyAttrs": "",