Skip to content

Commit f6b5318

Browse files
committed
update
1 parent bda2391 commit f6b5318

3 files changed

Lines changed: 106 additions & 115 deletions

File tree

src/assets/vender/copy.svg

Lines changed: 3 additions & 0 deletions
Loading

src/iconify.test.ts

Lines changed: 99 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,127 @@
11
import path from 'node:path'
22

3-
import { afterEach, describe, expect, it, vi } from 'vitest'
3+
import { describe, expect, it } from 'vitest'
44

55
import { importSvgCollection, importSvgCollections } from './iconify'
66

77
const assetsPath = path.resolve(__dirname, 'assets')
88

99
describe('importSvgCollection', () => {
10-
afterEach(() => {
11-
vi.restoreAllMocks()
12-
})
13-
14-
it('should import SVG collection and derive prefix from directory name', async () => {
15-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
16-
17-
const result = await importSvgCollection({
18-
source: path.join(assetsPath, 'vender/line/arrows'),
19-
})
20-
21-
// Prefix should be derived from directory name
22-
expect(result.prefix).toBe('arrows')
23-
// Icons with non-black/white colors should be skipped
24-
expect(Object.keys(result.icons).length).toBe(0)
25-
expect(warnSpy).toHaveBeenCalled()
26-
})
27-
28-
it('should skip icons with invalid colors and warn', async () => {
29-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
30-
10+
it('should import SVG collection', async () => {
3111
const result = await importSvgCollection({
32-
source: path.join(assetsPath, 'vender/line/alertsAndFeedback'),
12+
source: path.join(assetsPath, 'vender/line'),
3313
})
3414

35-
expect(result.prefix).toBe('alertsAndFeedback')
36-
expect(Object.keys(result.icons).length).toBe(0)
37-
expect(warnSpy).toHaveBeenCalled()
15+
expect(result).toMatchInlineSnapshot(`
16+
{
17+
"icons": {
18+
"alert-triangle": {
19+
"body": "<path fill="none" stroke="#f79009" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.25" d="M8 5.333V8m0 2.666h.006M6.86 1.906l-5.647 9.427a1.334 1.334 0 0 0 1.14 2h11.293a1.333 1.333 0 0 0 1.14-2L9.14 1.906a1.333 1.333 0 0 0-2.28 0"/>",
20+
},
21+
"arrow-narrow-left": {
22+
"body": "<path fill="none" stroke="#155eef" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.363 8H2.696m0 0l4 4m-4-4l4-4"/>",
23+
"width": 17,
24+
},
25+
"arrow-up-right": {
26+
"body": "<path fill="none" stroke="#667085" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.25" d="m4.083 9.917l5.834-5.834m0 0H4.083m5.834 0v5.834"/>",
27+
"height": 14,
28+
"width": 14,
29+
},
30+
"thumbs-down": {
31+
"body": "<g fill="none"><g clip-path="url(#svgID0)"><path stroke="#667085" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11.333 1.334v7.333m3.334-2.133V3.467c0-.746 0-1.12-.146-1.405a1.33 1.33 0 0 0-.582-.583c-.286-.145-.659-.145-1.406-.145H5.412c-.974 0-1.462 0-1.855.178a2 2 0 0 0-.85.73c-.236.36-.31.842-.458 1.805L1.9 6.314c-.195 1.27-.293 1.905-.104 2.4a2 2 0 0 0 .88 1.025c.46.262 1.102.262 2.387.262H5.6c.373 0 .56 0 .703.072a.67.67 0 0 1 .291.292c.073.142.073.329.073.702v1.956c0 .908.736 1.644 1.644 1.644a.55.55 0 0 0 .5-.325l2.24-5.041c.103-.23.154-.344.234-.428a.7.7 0 0 1 .256-.166c.11-.04.235-.04.486-.04h.506c.747 0 1.12 0 1.406-.145c.25-.128.454-.332.582-.583c.146-.285.146-.658.146-1.405"/></g><defs><clipPath id="svgID0"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></g>",
32+
},
33+
},
34+
"lastModified": 1769863401,
35+
"prefix": "line",
36+
}
37+
`)
3838
})
3939

40-
it('should import nested directory structure', async () => {
41-
vi.spyOn(console, 'warn').mockImplementation(() => {})
42-
40+
it('should import SVG collection without subdirectories', async () => {
4341
const result = await importSvgCollection({
4442
source: path.join(assetsPath, 'vender'),
43+
includeSubDirs: false,
4544
})
4645

47-
expect(result.prefix).toBe('vender')
48-
expect(result.icons).toBeDefined()
49-
})
50-
51-
it('should return valid IconifyJSON structure', async () => {
52-
vi.spyOn(console, 'warn').mockImplementation(() => {})
53-
54-
const result = await importSvgCollection({
55-
source: path.join(assetsPath, 'vender/pipeline'),
56-
})
57-
58-
expect(result).toHaveProperty('prefix', 'pipeline')
59-
expect(result).toHaveProperty('icons')
60-
expect(typeof result.icons).toBe('object')
61-
})
62-
63-
it('should skip invalid SVG files and warn', async () => {
64-
vi.spyOn(console, 'warn').mockImplementation(() => {})
65-
66-
const result = await importSvgCollection({
67-
source: path.join(assetsPath, 'public/avatar'),
68-
})
69-
70-
expect(result.prefix).toBe('avatar')
71-
expect(result.icons).toBeDefined()
46+
expect(result).toMatchInlineSnapshot(`
47+
{
48+
"icons": {
49+
"copy": {
50+
"body": "<path fill="none" stroke="#667085" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.667 2.666H12A1.333 1.333 0 0 1 13.333 4v9.333A1.333 1.333 0 0 1 12 14.666H4a1.333 1.333 0 0 1-1.333-1.333V4A1.333 1.333 0 0 1 4 2.666h1.333M6 1.333h4c.368 0 .666.298.666.667v1.333A.667.667 0 0 1 10 4H6a.667.667 0 0 1-.667-.667V2c0-.369.299-.667.667-.667"/>",
51+
},
52+
},
53+
"lastModified": 1769863401,
54+
"prefix": "vender",
55+
}
56+
`)
7257
})
7358
})
7459

7560
describe('importSvgCollections', () => {
76-
afterEach(() => {
77-
vi.restoreAllMocks()
78-
})
79-
8061
it('should import nested directory structure as separate collections', async () => {
81-
vi.spyOn(console, 'warn').mockImplementation(() => {})
82-
83-
const result = await importSvgCollections({
84-
source: assetsPath,
85-
})
86-
87-
expect(result).toBeDefined()
88-
expect(typeof result).toBe('object')
89-
})
90-
91-
it('should use folder name as prefix for each collection', async () => {
92-
vi.spyOn(console, 'warn').mockImplementation(() => {})
93-
9462
const result = await importSvgCollections({
9563
source: assetsPath,
9664
})
9765

98-
// Each collection's prefix should match its folder name
99-
for (const [key, collection] of Object.entries(result)) {
100-
expect(collection.prefix).toBe(key)
101-
}
102-
})
103-
104-
it('should skip icons with non-black/white colors', async () => {
105-
vi.spyOn(console, 'warn').mockImplementation(() => {})
106-
107-
const result = await importSvgCollections({
108-
source: path.join(assetsPath, 'vender'),
109-
})
110-
111-
// Icons with non-black/white colors should be skipped
112-
// Collections with no valid icons won't be included
113-
const arrowsCollection = result.arrows
114-
expect(arrowsCollection?.icons ? Object.keys(arrowsCollection.icons).length : 0).toBe(0)
115-
})
116-
117-
it('should return valid IconifyJSON structure for each collection', async () => {
118-
vi.spyOn(console, 'warn').mockImplementation(() => {})
119-
120-
const result = await importSvgCollections({
121-
source: assetsPath,
122-
})
123-
124-
for (const [prefix, collection] of Object.entries(result)) {
125-
expect(collection).toHaveProperty('prefix', prefix)
126-
expect(collection).toHaveProperty('icons')
127-
expect(typeof collection.icons).toBe('object')
128-
}
129-
})
130-
131-
it('should work with subset of directory tree', async () => {
132-
vi.spyOn(console, 'warn').mockImplementation(() => {})
133-
134-
const result = await importSvgCollections({
135-
source: path.join(assetsPath, 'vender/line'),
136-
})
137-
138-
expect(result).toBeDefined()
139-
expect(typeof result).toBe('object')
66+
expect(result).toMatchInlineSnapshot(`
67+
{
68+
"public-avatar": {
69+
"icons": {
70+
"user": {
71+
"body": "<g fill="none"><g clip-path="url(#svgID0)"><rect width="512" height="512" fill="#b2ddff" rx="256"/><circle cx="256" cy="196" r="84" fill="#fff" opacity=".68"/><ellipse cx="256" cy="583.5" fill="#fff" opacity=".68" rx="266" ry="274.5"/></g><defs><clipPath id="svgID0"><rect width="512" height="512" fill="#fff" rx="256"/></clipPath></defs></g>",
72+
"height": 512,
73+
"width": 512,
74+
},
75+
},
76+
"lastModified": 1769863401,
77+
"prefix": "public-avatar",
78+
},
79+
"vender": {
80+
"icons": {
81+
"copy": {
82+
"body": "<path fill="none" stroke="#667085" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.667 2.666H12A1.333 1.333 0 0 1 13.333 4v9.333A1.333 1.333 0 0 1 12 14.666H4a1.333 1.333 0 0 1-1.333-1.333V4A1.333 1.333 0 0 1 4 2.666h1.333M6 1.333h4c.368 0 .666.298.666.667v1.333A.667.667 0 0 1 10 4H6a.667.667 0 0 1-.667-.667V2c0-.369.299-.667.667-.667"/>",
83+
},
84+
},
85+
"lastModified": 1769863401,
86+
"prefix": "vender",
87+
},
88+
"vender-line-alertsAndFeedback": {
89+
"icons": {
90+
"alert-triangle": {
91+
"body": "<path fill="none" stroke="#f79009" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.25" d="M8 5.333V8m0 2.666h.006M6.86 1.906l-5.647 9.427a1.334 1.334 0 0 0 1.14 2h11.293a1.333 1.333 0 0 0 1.14-2L9.14 1.906a1.333 1.333 0 0 0-2.28 0"/>",
92+
},
93+
"thumbs-down": {
94+
"body": "<g fill="none"><g clip-path="url(#svgID0)"><path stroke="#667085" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11.333 1.334v7.333m3.334-2.133V3.467c0-.746 0-1.12-.146-1.405a1.33 1.33 0 0 0-.582-.583c-.286-.145-.659-.145-1.406-.145H5.412c-.974 0-1.462 0-1.855.178a2 2 0 0 0-.85.73c-.236.36-.31.842-.458 1.805L1.9 6.314c-.195 1.27-.293 1.905-.104 2.4a2 2 0 0 0 .88 1.025c.46.262 1.102.262 2.387.262H5.6c.373 0 .56 0 .703.072a.67.67 0 0 1 .291.292c.073.142.073.329.073.702v1.956c0 .908.736 1.644 1.644 1.644a.55.55 0 0 0 .5-.325l2.24-5.041c.103-.23.154-.344.234-.428a.7.7 0 0 1 .256-.166c.11-.04.235-.04.486-.04h.506c.747 0 1.12 0 1.406-.145c.25-.128.454-.332.582-.583c.146-.285.146-.658.146-1.405"/></g><defs><clipPath id="svgID0"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></g>",
95+
},
96+
},
97+
"lastModified": 1769863401,
98+
"prefix": "vender-line-alertsAndFeedback",
99+
},
100+
"vender-line-arrows": {
101+
"icons": {
102+
"arrow-narrow-left": {
103+
"body": "<path fill="none" stroke="#155eef" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.363 8H2.696m0 0l4 4m-4-4l4-4"/>",
104+
"width": 17,
105+
},
106+
"arrow-up-right": {
107+
"body": "<path fill="none" stroke="#667085" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.25" d="m4.083 9.917l5.834-5.834m0 0H4.083m5.834 0v5.834"/>",
108+
"height": 14,
109+
"width": 14,
110+
},
111+
},
112+
"lastModified": 1769863401,
113+
"prefix": "vender-line-arrows",
114+
},
115+
"vender-pipeline": {
116+
"icons": {
117+
"input-field": {
118+
"body": "<g fill="#354052"><path fill-rule="evenodd" d="M11.333 1.667a.667.667 0 0 0-1.333 0v12.666a.667.667 0 1 0 1.333 0v-1h1.334a2 2 0 0 0 2-2V4.667a2 2 0 0 0-2-2h-1.334zM12.667 12h-1.334V4h1.334c.368 0 .666.298.666.667v6.666a.667.667 0 0 1-.666.667" clip-rule="evenodd"/><path d="M8.667 13.333V12H3.333a.667.667 0 0 1-.666-.667V4.667c0-.369.298-.667.666-.667h5.334V2.667H3.333a2 2 0 0 0-2 2v6.666a2 2 0 0 0 2 2z"/><path d="M8.667 5.249a1 1 0 0 0-.106-.004l-.046.002c-.53.02-1.028.267-1.363.68l-.572.708l-.138-.413a1.25 1.25 0 0 0-1.235-.847l-.726.028a.667.667 0 0 0 .052 1.333l.666-.026l.386 1.155l-1.108 1.37a.5.5 0 0 1-.377.186l-.047.002a.667.667 0 1 0 .052 1.332l.047-.002a1.84 1.84 0 0 0 1.362-.68l.573-.708l.138.413a1.25 1.25 0 0 0 1.257.846l1.185-.087V9.2l-1.2.088l-.386-1.153l1.108-1.37a.5.5 0 0 1 .377-.186l.047-.002l.054-.004z"/></g>",
119+
},
120+
},
121+
"lastModified": 1769863401,
122+
"prefix": "vender-pipeline",
123+
},
124+
}
125+
`)
140126
})
141127
})

src/iconify.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ async function processIconSet(iconSet: IconSet): Promise<void> {
8080
return 'remove'
8181
}
8282

83-
throw new Error(`Unexpected color "${colorStr}" in attribute ${attr}`)
83+
// Icon is not monotone
84+
return color
8485
},
8586
})
8687

@@ -216,7 +217,8 @@ export async function importSvgCollections(
216217
const collections: Record<string, IconifyJSON> = {}
217218

218219
for (const dir of svgDirs) {
219-
const prefix = path.basename(dir)
220+
const relativePath = path.relative(source, dir)
221+
const prefix = relativePath.split(path.sep).join('-')
220222

221223
const iconSet = await importDirectory(dir, {
222224
prefix,

0 commit comments

Comments
 (0)