Skip to content

Commit faa915c

Browse files
authored
feat: support arbitrary names for custom icons, fix #265 (#317)
1 parent 65e4c85 commit faa915c

File tree

7 files changed

+120
-3
lines changed

7 files changed

+120
-3
lines changed

README.md

+25
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,31 @@ export default defineNuxtConfig({
187187
})
188188
```
189189

190+
### Case Sensitive Custom Collections
191+
192+
Before `v1.10`, due to the limitation of Iconify's previous convention, all custom icons were normalized to `kebab-case` with a warning. Thanks to the updates on Iconify side, starting from `v1.10`, you can opt-in to use case-sensitive custom collections and by pass the normalization.
193+
194+
```ts
195+
export default defineNuxtConfig({
196+
modules: [
197+
'@nuxt/icon'
198+
],
199+
icon: {
200+
customCollections: [
201+
{
202+
prefix: 'my-icon',
203+
dir: './assets/my-icons',
204+
normalizeIconName: false, // <-- this
205+
},
206+
],
207+
},
208+
})
209+
```
210+
211+
Which enable to use `assets/my-icons/FooBar.svg` as `my-icon:FooBar`, for example.
212+
213+
`normalizeIconName` is default to `true` for backward compatibility, and will be flipped in the future major version. See [#265](https://github.com/nuxt/icon/issues/265) for more context.
214+
190215
### Icon Customization
191216

192217
To update the default size (`1em`) of the `<Icon />`, create an `app.config.ts` with the `icon.size` property.

playground/components/ShowcaseFixture.vue

+12
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ defineProps<{
7777
:mode
7878
:customize
7979
/>
80+
<Icon
81+
name="custom1:Foo_Bar_Zag"
82+
size="64"
83+
:mode
84+
:customize
85+
/>
86+
<Icon
87+
name="custom1:Foo.BarZag"
88+
size="64"
89+
:mode
90+
:customize
91+
/>
8092
</p>
8193
<p>
8294
Custom icons with `prefix: ''` from local fs:
+8
Loading

playground/nuxt.config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,17 @@ export default defineNuxtConfig({
4040
{
4141
prefix: 'custom1',
4242
dir: './icons/custom1',
43+
normalizeIconName: false,
4344
},
4445
{
4546
prefix: 'custom-parts',
4647
dir: './icons/custom-parts',
48+
normalizeIconName: false,
4749
},
4850
{
4951
prefix: '',
5052
dir: './icons/no-prefix',
53+
normalizeIconName: false,
5154
},
5255
],
5356
serverBundle: 'remote',

playground/test/nuxt/output/fixtures.html

+50
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,56 @@
195195
d="M281.44 397.667H438.32C443.326 397.667 448.118 395.908 452.453 393.427C456.789 390.946 461.258 387.831 463.76 383.533C466.262 379.236 468.002 374.36 468 369.399C467.998 364.437 466.266 359.563 463.76 355.268L357.76 172.947C355.258 168.65 352.201 165.534 347.867 163.053C343.532 160.573 337.325 158.813 332.32 158.813C327.315 158.813 322.521 160.573 318.187 163.053C313.852 165.534 310.795 168.65 308.293 172.947L281.44 219.587L227.733 129.13C225.229 124.834 222.176 120.307 217.84 117.827C213.504 115.346 208.713 115 203.707 115C198.701 115 193.909 115.346 189.573 117.827C185.238 120.307 180.771 124.834 178.267 129.13L46.8267 355.268C44.3208 359.563 44.0022 364.437 44 369.399C43.9978 374.36 44.3246 379.235 46.8267 383.533C49.3288 387.83 53.7979 390.946 58.1333 393.427C62.4688 395.908 67.2603 397.667 72.2667 397.667H171.2C210.401 397.667 238.934 380.082 258.827 346.787L306.88 263.4L332.32 219.587L410.053 352.44H306.88L281.44 397.667ZM169.787 352.44H100.533L203.707 174.36L256 263.4L221.361 323.784C208.151 345.387 193.089 352.44 169.787 352.44Z"
196196
fill="#00DC82"
197197
></path>
198+
</g></svg
199+
><svg
200+
xmlns="http://www.w3.org/2000/svg"
201+
xmlns:xlink="http://www.w3.org/1999/xlink"
202+
aria-hidden="true"
203+
role="img"
204+
class="iconify iconify--custom1"
205+
style="font-size: 64px"
206+
width="1.36em"
207+
height="1em"
208+
viewBox="0 0 256 189"
209+
>
210+
<g>
211+
<polygon
212+
fill="#48B884"
213+
points="109.7766 0.0009 -0.0004 188.2259 219.5526 188.2259"
214+
></polygon>
215+
<polygon
216+
fill="#3F7F70"
217+
points="72.8361 188.226 255.9991 188.226 164.4181 31.198"
218+
></polygon>
219+
<polygon
220+
fill="#36495D"
221+
points="72.8361 188.226 219.5521 188.226 146.1941 62.444"
222+
></polygon>
223+
</g></svg
224+
><svg
225+
xmlns="http://www.w3.org/2000/svg"
226+
xmlns:xlink="http://www.w3.org/1999/xlink"
227+
aria-hidden="true"
228+
role="img"
229+
class="iconify iconify--custom1"
230+
style="font-size: 64px"
231+
width="1.36em"
232+
height="1em"
233+
viewBox="0 0 256 189"
234+
>
235+
<g>
236+
<polygon
237+
fill="#48B884"
238+
points="109.7766 0.0009 -0.0004 188.2259 219.5526 188.2259"
239+
></polygon>
240+
<polygon
241+
fill="#3F7F70"
242+
points="72.8361 188.226 255.9991 188.226 164.4181 31.198"
243+
></polygon>
244+
<polygon
245+
fill="#36495D"
246+
points="72.8361 188.226 219.5521 188.226 146.1941 62.444"
247+
></polygon>
198248
</g>
199249
</svg>
200250
</p>

src/collections.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function getCollectionPath(collection: string) {
3131
}
3232

3333
// https://github.com/iconify/iconify/blob/2274c033b49c01a50dc89b490b89d803d19d95dc/packages/utils/src/icon/name.ts#L15-L18
34-
export const validIconNameRE = /^[a-z0-9]+(-[a-z0-9]+)*$/
34+
const validIconNameRE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
3535

3636
export async function loadCustomCollection(collection: CustomCollection, nuxt: Nuxt): Promise<IconifyJSON> {
3737
const dir = isAbsolute(collection.dir)
@@ -45,19 +45,25 @@ export async function loadCustomCollection(collection: CustomCollection, nuxt: N
4545
}))
4646
.sort()
4747

48+
const {
49+
// TODO: next major flip this
50+
normalizeIconName = true,
51+
} = collection
52+
4853
const parsedIcons = await Promise.all(files.map(async (file) => {
4954
let name = basename(file, '.svg')
5055

5156
// Currently Iconify only supports kebab-case icon names
5257
// https://github.com/nuxt/icon/issues/265#issuecomment-2441604639
5358
// We normalize the icon name to kebab-case and warn user about it
54-
if (!validIconNameRE.test(name)) {
59+
if (normalizeIconName && !validIconNameRE.test(name)) {
5560
const normalized = name
5661
.replace(/([a-z])([A-Z])/g, '$1-$2')
5762
.toLowerCase()
5863
.replace(/[^a-z0-9-]/g, '-')
5964
.replace(/-+/g, '-')
60-
logger.warn(`Custom icon \`${name}\` is normalized to \`${normalized}\`, we recommend to change the file name to match the icon name`)
65+
if (normalized !== name)
66+
logger.warn(`Custom icon \`${name}\` is normalized to \`${normalized}\`, we recommend to change the file name to match the icon name, or pass \`normalizeIconName: false\` to your custom collection options`)
6167
name = normalized
6268
}
6369

src/types.ts

+13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ export interface ModuleOptions extends Partial<Omit<NuxtIconRuntimeOptions, 'cus
5454

5555
export interface CustomCollection extends Pick<IconifyJSON, 'prefix' | 'width' | 'height'> {
5656
dir: string
57+
/**
58+
* Normalize icon names to kebab-case
59+
*
60+
* Since v1.10.0, Iconify supports arbitrary icon names.
61+
* You can disable this option to keep the original icon names.
62+
*
63+
* This options is true by default to ensure compatibility with older versions.
64+
* In the next major version, this option will be disabled by default.
65+
*
66+
* @see https://github.com/nuxt/icon/issues/265#issuecomment-2524979176
67+
* @default true
68+
*/
69+
normalizeIconName?: boolean
5770
}
5871

5972
export interface RemoteCollection {

0 commit comments

Comments
 (0)