From 34ea5ae2f294c19bc84fa92c0521568f5d024d55 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 7 Jun 2024 15:24:56 +0100 Subject: [PATCH] fix(nuxt): deduplicate `app.head` arrays (#27480) --- packages/nuxt/src/core/nuxt.ts | 21 +++++++++++++++++++ test/basic.test.ts | 1 + .../fixtures/basic/extends/bar/nuxt.config.ts | 8 +++++++ 3 files changed, 30 insertions(+) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 228eaa423a..8b5d389967 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -7,6 +7,7 @@ import { resolvePath as _resolvePath } from 'mlly' import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions, RuntimeConfig } from 'nuxt/schema' import type { PackageJson } from 'pkg-types' import { readPackageJSON, resolvePackageJSON } from 'pkg-types' +import { hash } from 'ohash' import escapeRE from 'escape-string-regexp' import fse from 'fs-extra' @@ -583,6 +584,11 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise { options.appDir = options.alias['#app'] = resolve(distDir, 'app') options._majorVersion = 3 + // De-duplicate key arrays + for (const key in options.app.head || {}) { + options.app.head[key as 'link'] = deduplicateArray(options.app.head[key as 'link']) + } + // Nuxt DevTools only works for Vite if (options.builder === '@nuxt/vite-builder') { const isDevToolsEnabled = typeof options.devtools === 'boolean' @@ -668,3 +674,18 @@ async function checkDependencyVersion (name: string, nuxtVersion: string): Promi } const RESTART_RE = /^(?:app|error|app\.config)\.(?:js|ts|mjs|jsx|tsx|vue)$/i + +function deduplicateArray (maybeArray: T): T { + if (!Array.isArray(maybeArray)) { return maybeArray } + + const fresh = [] + const hashes = new Set() + for (const item of maybeArray) { + const _hash = hash(item) + if (!hashes.has(_hash)) { + hashes.add(_hash) + fresh.push(item) + } + } + return fresh as T +} diff --git a/test/basic.test.ts b/test/basic.test.ts index da188dfc68..1e4db83c89 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -64,6 +64,7 @@ describe('route rules', () => { expect(headHtml).toContain('') expect(headHtml).toContain('') expect(headHtml).toContain('') + expect(headHtml.match(//g)).toHaveLength(1) const { script, attrs } = parseData(headHtml) expect(script.serverRendered).toEqual(false) diff --git a/test/fixtures/basic/extends/bar/nuxt.config.ts b/test/fixtures/basic/extends/bar/nuxt.config.ts index 680205b4f0..f32e267e8f 100644 --- a/test/fixtures/basic/extends/bar/nuxt.config.ts +++ b/test/fixtures/basic/extends/bar/nuxt.config.ts @@ -1,3 +1,11 @@ export default defineNuxtConfig({ modules: [undefined], + app: { + head: { + meta: [ + { name: 'viewport', content: 'width=1024, initial-scale=1' }, + { charset: 'utf-8' }, + ], + }, + }, })