Skip to content

Commit

Permalink
Fix deduplicating dynamic (array) meta tags
Browse files Browse the repository at this point in the history
  • Loading branch information
sheerun committed Nov 26, 2019
1 parent 5030bda commit b320632
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 19 deletions.
47 changes: 28 additions & 19 deletions packages/next/next-server/lib/head.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ type WithInAmpMode = {
}

export function defaultHead(inAmpMode = false) {
const head = [<meta key="charSet" charSet="utf-8" />]
const head = [<meta charSet="utf-8" />]
if (!inAmpMode) {
head.push(
<meta
key="viewport"
name="viewport"
content="width=device-width,minimum-scale=1,initial-scale=1"
/>
Expand Down Expand Up @@ -67,43 +66,53 @@ function unique() {
const metaCategories: { [metatype: string]: Set<string> } = {}

return (h: React.ReactElement<any>) => {
if (h.key && typeof h.key !== 'number' && h.key.indexOf('.$') === 0) {
if (keys.has(h.key)) return false
keys.add(h.key)
return true
}
let unique = true

// If custom meta tag has been added the key will be prepended with `.$`, we can
// check for this and dedupe in favor of the custom one, so the default
// is not rendered as well
if (keys.has(`.$${h.key}`)) return false
if (h.key && typeof h.key !== 'number' && h.key.indexOf('$') > 0) {
const key = h.key.slice(h.key.indexOf('$') + 1)
if (keys.has(key)) {
unique = false
} else {
keys.add(key)
}
}

// eslint-disable-next-line default-case
switch (h.type) {
case 'title':
case 'base':
if (tags.has(h.type)) return false
tags.add(h.type)
if (tags.has(h.type)) {
unique = false
} else {
tags.add(h.type)
}
break
case 'meta':
for (let i = 0, len = METATYPES.length; i < len; i++) {
const metatype = METATYPES[i]
if (!h.props.hasOwnProperty(metatype)) continue

if (metatype === 'charSet') {
if (metaTypes.has(metatype)) return false
metaTypes.add(metatype)
if (metaTypes.has(metatype)) {
unique = false
} else {
metaTypes.add(metatype)
}
} else {
const category = h.props[metatype]
const categories = metaCategories[metatype] || new Set()
if (categories.has(category)) return false
categories.add(category)
metaCategories[metatype] = categories
if (categories.has(category)) {
unique = false
} else {
categories.add(category)
metaCategories[metatype] = categories
}
}
}
break
}
return true

return unique
}
}

Expand Down
19 changes: 19 additions & 0 deletions test/integration/client-navigation/pages/head.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,27 @@ export default () => (
<link rel="stylesheet" href="/dup-style.css" />

{/* only one tag will be rendered as they have the same key */}

<link rel="stylesheet" href="dedupe-style.css" key="my-style" />
<link rel="stylesheet" href="dedupe-style.css" key="my-style" />

{/* such style can be used for alternate links on _app vs individual pages */}
{['pl', 'en'].map(language => (
<link
rel="alternate"
key={language}
hrefLang={language}
href={'/first/' + language}
/>
))}
{['pl', 'en'].map(language => (
<link
rel="alternate"
key={language}
hrefLang={language}
href={'/last/' + language}
/>
))}
</Head>
<h1>I can have meta tags</h1>
</div>
Expand Down
6 changes: 6 additions & 0 deletions test/integration/client-navigation/test/rendering.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ export default function(render, fetch) {
expect(html).not.toContain(
'<link rel="stylesheet" href="dedupe-style.css"/><link rel="stylesheet" href="dedupe-style.css"/>'
)
expect(html).toContain(
'<link rel="alternate" hrefLang="en" href="/last/en"/>'
)
expect(html).not.toContain(
'<link rel="alternate" hrefLang="en" href="/first/en"/>'
)
})

test('header helper dedupes tags with the same key as the default', async () => {
Expand Down

0 comments on commit b320632

Please sign in to comment.