Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { existsSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { unpublishedAgentPaths } from './layers/marketing/app/utils/agent-pages'
import pkg from './package.json'
import { dependencyPluginCompat } from './scripts/lib/dependency-plugin-compat'
import { withBuildAssetMissFallthrough } from './scripts/lib/static-asset-fallthrough'
import { externalCheckin } from './shared/checkin-external'
import { iconifyCollections } from './shared/icon-collections'
import { SENTRY_DSN, sentryRelease, sentryReportingEnabled } from './shared/sentry'

const iconCollections = iconifyCollections(pkg)

const hasSentryAuthToken = Boolean(process.env.SENTRY_AUTH_TOKEN)
|| existsSync('.env.sentry-build-plugin')

Expand Down Expand Up @@ -608,6 +612,7 @@ export default defineNuxtConfig({
// inlines just those. The Iconify API fallback is off, so a visitor's browser
// never calls api.iconify.design. See docs/ops/bundle-baseline-2026-07-23.md.
icon: {
collections: iconCollections,
serverBundle: false,
fallbackToApi: false,
clientBundle: {
Expand Down
26 changes: 26 additions & 0 deletions shared/icon-collections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* The collection names `icon.collections` in nuxt.config.ts must list.
*
* With `serverBundle: false` and no API fallback, @nuxt/icon registers no
* collection names, so it splits `i-vscode-icons-foo` at the first hyphen and
* looks up collection `vscode`. The icon never resolves: SSR ships a bare class
* and logs a warning. Naming every installed collection fixes the split.
*
* Only `dependencies` count. An `@iconify-json/*` package that slips into
* `devDependencies` silently drops its collection here, which is exactly the
* failure above, so every icon package this site renders with must stay a
* runtime dependency.
*/

const ICONIFY_PREFIX = '@iconify-json/'

export interface PackageManifest {
dependencies?: Record<string, string>
devDependencies?: Record<string, string>
}

export function iconifyCollections(pkg: PackageManifest): string[] {
return Object.keys(pkg.dependencies ?? {})
.filter(name => name.startsWith(ICONIFY_PREFIX))
.map(name => name.slice(ICONIFY_PREFIX.length))
}
34 changes: 34 additions & 0 deletions test/unit/icon-collections.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @vitest-environment node
import { describe, expect, it } from 'vitest'
import { iconifyCollections } from '#shared/icon-collections'

describe('iconifyCollections', () => {
it('names every installed collection in dependency order', () => {
expect(iconifyCollections({
dependencies: {
'@iconify-json/vscode-icons': 'catalog:',
'@iconify-json/simple-icons': 'catalog:',
},
})).toEqual(['vscode-icons', 'simple-icons'])
})

it('ignores dependencies that are not icon collections', () => {
expect(iconifyCollections({
dependencies: {
'nuxt': 'catalog:',
'@iconify-json/lucide': 'catalog:',
},
})).toEqual(['lucide'])
})

it('excludes an icon package that only sits in devDependencies', () => {
expect(iconifyCollections({
dependencies: { '@iconify-json/lucide': 'catalog:' },
devDependencies: { '@iconify-json/vscode-icons': 'catalog:' },
})).toEqual(['lucide'])
})

it('names nothing when no icon packages are installed', () => {
expect(iconifyCollections({ dependencies: { nuxt: 'catalog:' } })).toEqual([])
})
})