Skip to content

Commit 0f628c8

Browse files
authored
perf: look up icon collections by hyphen-separated prefix (#561)
1 parent 7913fb9 commit 0f628c8

2 files changed

Lines changed: 32 additions & 17 deletions

File tree

‎src/runtime/components/resolve-name.ts‎

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
import { computed, reactive, toRaw } from 'vue'
22
import type { NuxtIconRuntimeOptions } from '../../types'
33

4-
const HYPHEN = 45
4+
const collectionSetCache = new WeakMap<string[], { readonly value: Set<string> }>()
55

6-
const sortedCollectionsCache = new WeakMap<string[], { readonly value: string[] }>()
7-
8-
export function getSortedCollections(collections: string[] | null | undefined): string[] {
6+
export function getCollectionSet(collections: string[] | null | undefined): Set<string> | undefined {
97
if (!collections)
10-
return []
8+
return
119
const key = toRaw(collections)
12-
let sorted = sortedCollectionsCache.get(key)
13-
if (!sorted) {
14-
const sort = (source: string[]) => [...source].sort((a, b) => b.length - a.length)
10+
let set = collectionSetCache.get(key)
11+
if (!set) {
1512
// App config is cloned per request on the server, but reactive and updated in place on the client (HMR, `updateAppConfig`)
16-
sorted = import.meta.client ? computed(() => sort(reactive(key))) : { value: sort(key) }
17-
sortedCollectionsCache.set(key, sorted)
13+
set = import.meta.client ? computed(() => new Set(reactive(key))) : { value: new Set(key) }
14+
collectionSetCache.set(key, set)
15+
}
16+
return set.value
17+
}
18+
19+
function findCollection(name: string, collections: Set<string>): string | undefined {
20+
let index = name.lastIndexOf('-')
21+
while (index > 0) {
22+
const collection = name.slice(0, index)
23+
if (collections.has(collection))
24+
return collection
25+
index = name.lastIndexOf('-', index - 1)
1826
}
19-
return sorted.value
2027
}
2128

2229
export function resolveName(name: string, options: Pick<NuxtIconRuntimeOptions, 'cssSelectorPrefix' | 'aliases' | 'collections'>): string {
@@ -28,8 +35,8 @@ export function resolveName(name: string, options: Pick<NuxtIconRuntimeOptions,
2835
// Disambiguate collection
2936
// e.g. `simple-icons-github` -> `simple-icons:github`
3037
if (!resolved.includes(':')) {
31-
const collection = getSortedCollections(options.collections)
32-
.find(c => resolved.charCodeAt(c.length) === HYPHEN && resolved.startsWith(c))
38+
const collections = getCollectionSet(options.collections)
39+
const collection = collections && findCollection(resolved, collections)
3340
return collection
3441
? collection + ':' + resolved.slice(collection.length + 1)
3542
: resolved

‎test/resolve-name.test.ts‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { computed, reactive } from 'vue'
22
import { describe, expect, it } from 'vitest'
3-
import { getSortedCollections, resolveName } from '../src/runtime/components/resolve-name'
3+
import { getCollectionSet, resolveName } from '../src/runtime/components/resolve-name'
44

55
function createOptions(collections: string[] | null, aliases: Record<string, string> = {}) {
66
return { cssSelectorPrefix: 'i-', aliases, collections }
@@ -19,6 +19,14 @@ describe('resolveName', () => {
1919
expect(resolveName('i-si-foo', options)).toBe('si:foo')
2020
})
2121

22+
it('prefers the longest collection regardless of configured order', () => {
23+
const options = createOptions(['a', 'a-b-c', 'a-b'])
24+
expect(resolveName('i-a-b-c-d', options)).toBe('a-b-c:d')
25+
expect(resolveName('i-a-b-x-d', options)).toBe('a-b:x-d')
26+
expect(resolveName('i-a-x-c-d', options)).toBe('a:x-c-d')
27+
expect(resolveName('i-a-b-c', options)).toBe('a-b:c')
28+
})
29+
2230
it('requires a hyphen after the collection name', () => {
2331
const options = createOptions(['simple', 'si'])
2432
expect(resolveName('i-simpleicons', options)).toBe('simpleicons')
@@ -74,9 +82,9 @@ describe('resolveName', () => {
7482
})
7583
})
7684

77-
describe('getSortedCollections', () => {
78-
it('sorts each collections array once', () => {
85+
describe('getCollectionSet', () => {
86+
it('creates one set per collections array', () => {
7987
const collections = ['simple', 'simple-icons']
80-
expect(getSortedCollections(collections)).toBe(getSortedCollections(collections))
88+
expect(getCollectionSet(collections)).toBe(getCollectionSet(collections))
8189
})
8290
})

0 commit comments

Comments
 (0)