website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


d272230 — pyrossh 2026-07-04T14:43:18+05:30
build: replace astro-icon with unplugin-icons

Generated files not shown: pnpm-lock.yaml

astro.config.mjs CHANGED
@@ -1,16 +1,28 @@
1
1
  // @ts-check
2
- import { defineConfig } from 'astro/config';
2
+ import { defineConfig } from "astro/config";
3
- import icon from "astro-icon";
3
+ import Icons from "unplugin-icons/vite";
4
- import sitemap from '@astrojs/sitemap';
4
+ import sitemap from "@astrojs/sitemap";
5
- import expressiveCode from 'astro-expressive-code';
5
+ import expressiveCode from "astro-expressive-code";
6
+ import cloudflare from "@astrojs/cloudflare";
6
7
 
7
8
  // https://astro.build/config
8
9
  export default defineConfig({
9
- site: 'https://pyrossh.dev',
10
+ site: "https://pyrossh.dev",
10
- trailingSlash: 'never',
11
+ trailingSlash: "never",
11
- output: 'static',
12
+ output: "server",
13
+ adapter: cloudflare(),
12
- integrations: [icon(), sitemap(), expressiveCode()],
14
+ integrations: [sitemap(), expressiveCode()],
13
15
  devToolbar: {
14
16
  enabled: false,
15
17
  },
18
+ vite: {
19
+ plugins: [Icons({ compiler: "astro" })],
20
+ ssr: {
21
+ external: ["svgo"],
22
+ // isomorphic-git pulls in a couple of legacy CJS-only packages; force
23
+ // Vite to bundle them so their `module.exports` interop is resolved
24
+ // at build time instead of relying on runtime globals.
25
+ // noExternal: ["isomorphic-git", "pify", "crc-32", "async-lock"],
26
+ },
27
+ },
16
- });
28
+ });
docs/superpowers/plans/2026-07-04-migrate-astro-icon-to-unplugin-icons.md ADDED
@@ -0,0 +1,398 @@
1
+ # Migrate astro-icon to unplugin-icons Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Replace the abandoned `astro-icon` with `unplugin-icons` for static icons and a lightweight `DynamicIcon` component for dynamic vscode-icons.
6
+
7
+ **Architecture:** Hybrid approach — static icons (material-symbols, mdi) use build-time imports via unplugin-icons; dynamic vscode-icons use a custom `DynamicIcon.astro` component that reads from the already-imported `@iconify-json/vscode-icons/icons.json` and renders SVG inline.
8
+
9
+ **Tech Stack:** unplugin-icons (Astro plugin), Astro 6, Iconify JSON collections
10
+
11
+ **Plan location:** docs/superpowers/plans/2026-07-04-migrate-astro-icon-to-unplugin-icons.md
12
+
13
+ ## Global Constraints
14
+
15
+ - Remove `astro-icon` entirely — no remaining references in config, deps, or source
16
+ - Two dynamic vscode-icons usages (RecursiveList.astro, FileLayout.astro) use the new `DynamicIcon` component
17
+ - All static icon imports follow unplugin-icons convention: `import Name from "~icons/collection/id"`
18
+ - Existing `@iconify-json/*` devDependencies stay unchanged
19
+ - Use `vp install` not `pnpm install` directly
20
+
21
+ ---
22
+
23
+ ### Task 1: Install unplugin-icons and update astro config
24
+
25
+ **Files:**
26
+
27
+ - Modify: `astro.config.mjs`
28
+ - Deps: add `unplugin-icons` (devDep)
29
+
30
+ - [ ] **Step 1: Install unplugin-icons**
31
+
32
+ ```bash
33
+ vp add -D unplugin-icons
34
+ ```
35
+
36
+ - [ ] **Step 2: Update astro.config.mjs**
37
+
38
+ Remove the `astro-icon` import, its integration, and the `optimizeDeps` entry. Add the `unplugin-icons/astro` integration.
39
+
40
+ ```js
41
+ // @ts-check
42
+ import { defineConfig } from "astro/config";
43
+ import Icons from "unplugin-icons/astro";
44
+ import sitemap from "@astrojs/sitemap";
45
+ import expressiveCode from "astro-expressive-code";
46
+ import cloudflare from "@astrojs/cloudflare";
47
+
48
+ // https://astro.build/config
49
+ export default defineConfig({
50
+ site: "https://pyrossh.dev",
51
+ trailingSlash: "never",
52
+ output: "server",
53
+ adapter: cloudflare(),
54
+ integrations: [Icons(), sitemap(), expressiveCode()],
55
+ devToolbar: {
56
+ enabled: false,
57
+ },
58
+ vite: {
59
+ ssr: {
60
+ external: ["svgo"],
61
+ },
62
+ },
63
+ });
64
+ ```
65
+
66
+ - [ ] **Step 3: Commit**
67
+
68
+ ```bash
69
+ git add astro.config.mjs package.json pnpm-lock.yaml
70
+ git commit -m "build: replace astro-icon with unplugin-icons"
71
+ ```
72
+
73
+ ---
74
+
75
+ ### Task 2: Create DynamicIcon component
76
+
77
+ **Files:**
78
+
79
+ - Create: `src/components/DynamicIcon.astro`
80
+
81
+ - [ ] **Step 1: Create DynamicIcon.astro**
82
+
83
+ ```astro
84
+ ---
85
+ import allIcons from "@iconify-json/vscode-icons/icons.json" assert { type: "json" };
86
+
87
+ export interface Props {
88
+ name: string;
89
+ size?: string;
90
+ color?: string;
91
+ }
92
+
93
+ const { name, size = "24px", color = "currentColor" } = Astro.props;
94
+ const iconData = allIcons.icons[name as keyof typeof allIcons.icons] ?? allIcons.icons["default-file"];
95
+ const svgPath = iconData?.body ?? "";
96
+ ---
97
+
98
+ <svg
99
+ xmlns="http://www.w3.org/2000/svg"
100
+ width={size}
101
+ height={size}
102
+ viewBox="0 0 24 24"
103
+ fill="none"
104
+ stroke="currentColor"
105
+ stroke-linecap="round"
106
+ stroke-linejoin="round"
107
+ color={color}
108
+ set:html={svgPath}
109
+ />
110
+ ```
111
+
112
+ - [ ] **Step 2: Commit**
113
+
114
+ ```bash
115
+ git add src/components/DynamicIcon.astro
116
+ git commit -m "feat: add DynamicIcon component for vscode-icons"
117
+ ```
118
+
119
+ ---
120
+
121
+ ### Task 3: Update Header.astro
122
+
123
+ **Files:**
124
+
125
+ - Modify: `src/components/Header.astro`
126
+
127
+ - [ ] **Step 1: Replace imports and Icon usages**
128
+
129
+ Remove `import { Icon } from "astro-icon/components";`. Add unplugin-icons imports. Replace `<Icon>` with the imported components.
130
+
131
+ ```astro
132
+ ---
133
+ import MdiSunnyRounded from "~icons/material-symbols/sunny-rounded";
134
+ import MdiMoonStars from "~icons/material-symbols/moon-stars";
135
+ import { ThemeSwitch } from "astro-color-scheme";
136
+ import HeaderLink from "./HeaderLink.astro";
137
+ import Wrapper from "./Wrapper.astro";
138
+ ---
139
+ ```
140
+
141
+ Replace the two `<Icon>` elements:
142
+
143
+ ```astro
144
+ <Icon
145
+ style="display: var(--btn-light);"
146
+ name="material-symbols:sunny-rounded"
147
+ />
148
+ <Icon
149
+ style="display: var(--btn-dark);"
150
+ name="material-symbols:moon-stars"
151
+ />
152
+ ```
153
+
154
+ Becomes:
155
+
156
+ ```astro
157
+ <MdiSunnyRounded
158
+ style="display: var(--btn-light);"
159
+ />
160
+ <MdiMoonStars
161
+ style="display: var(--btn-dark);"
162
+ />
163
+ ```
164
+
165
+ - [ ] **Step 2: Commit**
166
+
167
+ ```bash
168
+ git add src/components/Header.astro
169
+ git commit -m "refactor: migrate Header icons to unplugin-icons"
170
+ ```
171
+
172
+ ---
173
+
174
+ ### Task 4: Update RecursiveList.astro
175
+
176
+ **Files:**
177
+
178
+ - Modify: `src/components/RecursiveList.astro`
179
+
180
+ - [ ] **Step 1: Replace folder icon and dynamic icon**
181
+
182
+ Remove `import { Icon } from "astro-icon/components";` and `import allIcons from "@iconify-json/vscode-icons/icons.json" assert { type: "json" };`.
183
+
184
+ Add:
185
+
186
+ ```astro
187
+ import MdiFolder from "~icons/mdi/folder";
188
+ import DynamicIcon from "./DynamicIcon.astro";
189
+ ```
190
+
191
+ Replace the folder `<Icon>`:
192
+
193
+ ```astro
194
+ <Icon
195
+ name="mdi:folder"
196
+ size="24px"
197
+ color="hsl(45deg 75.37% 60%)"
198
+ />
199
+ ```
200
+
201
+ Becomes:
202
+
203
+ ```astro
204
+ <MdiFolder
205
+ style="color: hsl(45deg 75.37% 60%);"
206
+ width="24"
207
+ height="24"
208
+ />
209
+ ```
210
+
211
+ Replace the file icon `<Icon>`:
212
+
213
+ ```astro
214
+ <Icon name={`vscode-icons:${fileType}`} size="18px" />
215
+ ```
216
+
217
+ Becomes:
218
+
219
+ ```astro
220
+ <DynamicIcon name={fileType} size="18px" />
221
+ ```
222
+
223
+ Simplify the `fileType` variable (remove the `allIcons` lookup since DynamicIcon handles fallback):
224
+
225
+ ```astro
226
+ const iconName = resolveFileIcon(item.name, item.ext);
227
+ ```
228
+
229
+ Remove the `fileType` variable entirely and inline it — or keep it simple:
230
+
231
+ ```astro
232
+ const iconName = resolveFileIcon(item.name, item.ext);
233
+ ```
234
+
235
+ And change the usage to:
236
+
237
+ ```astro
238
+ <DynamicIcon name={`file-type-${iconName}`} size="18px" />
239
+ ```
240
+
241
+ - [ ] **Step 2: Commit**
242
+
243
+ ```bash
244
+ git add src/components/RecursiveList.astro
245
+ git commit -m "refactor: migrate RecursiveList icons to unplugin-icons + DynamicIcon"
246
+ ```
247
+
248
+ ---
249
+
250
+ ### Task 5: Update RepoLayout.astro
251
+
252
+ **Files:**
253
+
254
+ - Modify: `src/layouts/RepoLayout.astro`
255
+
256
+ - [ ] **Step 1: Replace info icon**
257
+
258
+ Remove `import { Icon } from "astro-icon/components";`. Add:
259
+
260
+ ```astro
261
+ import MdiInformationOutline from "~icons/mdi/information-outline";
262
+ ```
263
+
264
+ Replace:
265
+
266
+ ```astro
267
+ <Icon
268
+ name="mdi:information-outline"
269
+ size="18px"
270
+ color="var(--color-text)"
271
+ />
272
+ ```
273
+
274
+ Becomes:
275
+
276
+ ```astro
277
+ <MdiInformationOutline
278
+ width="18"
279
+ height="18"
280
+ color="var(--color-text)"
281
+ />
282
+ ```
283
+
284
+ - [ ] **Step 2: Commit**
285
+
286
+ ```bash
287
+ git add src/layouts/RepoLayout.astro
288
+ git commit -m "refactor: migrate RepoLayout icons to unplugin-icons"
289
+ ```
290
+
291
+ ---
292
+
293
+ ### Task 6: Update FileLayout.astro
294
+
295
+ **Files:**
296
+
297
+ - Modify: `src/layouts/FileLayout.astro`
298
+
299
+ - [ ] **Step 1: Replace dynamic icon + remove allIcons import**
300
+
301
+ Remove `import { Icon } from "astro-icon/components";` and `import allIcons from "@iconify-json/vscode-icons/icons.json" assert { type: "json" };`.
302
+
303
+ Add:
304
+
305
+ ```astro
306
+ import DynamicIcon from "@/components/DynamicIcon.astro";
307
+ ```
308
+
309
+ Simplify the icon logic — remove the `allIcons` lookup and `fileType` variable:
310
+
311
+ ```astro
312
+ const iconName = resolveFileIcon(file.name, file.ext);
313
+ ```
314
+
315
+ Replace the `<Icon>`:
316
+
317
+ ```astro
318
+ <Icon
319
+ name={`vscode-icons:${fileType}`}
320
+ size="24px"
321
+ color="hsl(0deg 75% 75%)"
322
+ />
323
+ ```
324
+
325
+ Becomes:
326
+
327
+ ```astro
328
+ <DynamicIcon name={`file-type-${iconName}`} size="24px" color="hsl(0deg 75% 75%)" />
329
+ ```
330
+
331
+ - [ ] **Step 2: Commit**
332
+
333
+ ```bash
334
+ git add src/layouts/FileLayout.astro
335
+ git commit -m "refactor: migrate FileLayout icons to DynamicIcon"
336
+ ```
337
+
338
+ ---
339
+
340
+ ### Task 7: Update cv/index.astro
341
+
342
+ **Files:**
343
+
344
+ - Modify: `src/pages/cv/index.astro`
345
+
346
+ - [ ] **Step 1: Replace external link icon**
347
+
348
+ Remove `import { Icon } from "astro-icon/components";`. Add:
349
+
350
+ ```astro
351
+ import MdiExternalLink from "~icons/mdi/external-link";
352
+ ```
353
+
354
+ Replace:
355
+
356
+ ```astro
357
+ <Icon name="mdi:external-link" size="18px" />
358
+ ```
359
+
360
+ Becomes:
361
+
362
+ ```astro
363
+ <MdiExternalLink width="18" height="18" />
364
+ ```
365
+
366
+ - [ ] **Step 2: Commit**
367
+
368
+ ```bash
369
+ git add src/pages/cv/index.astro
370
+ git commit -m "refactor: migrate CV page icons to unplugin-icons"
371
+ ```
372
+
373
+ ---
374
+
375
+ ### Task 8: Remove astro-icon from deps and verify
376
+
377
+ **Files:**
378
+
379
+ - Modify: `package.json`
380
+
381
+ - [ ] **Step 1: Remove astro-icon from dependencies**
382
+
383
+ Remove `"astro-icon": "^1.1.5",` from `package.json` `dependencies`.
384
+
385
+ - [ ] **Step 2: Run checks**
386
+
387
+ ```bash
388
+ vp check
389
+ ```
390
+
391
+ Fix any issues.
392
+
393
+ - [ ] **Step 3: Final commit**
394
+
395
+ ```bash
396
+ git add package.json
397
+ git commit -m "chore: remove astro-icon dependency"
398
+ ```
docs/superpowers/specs/2026-07-04-migrate-astro-icon-to-unplugin-icons-design.md ADDED
@@ -0,0 +1,51 @@
1
+ # Migrate astro-icon to unplugin-icons
2
+
3
+ ## Motivation
4
+
5
+ `astro-icon` is unmaintained and incompatible with Astro 6. Switch to `unplugin-icons` (actively maintained, framework-agnostic) for static icons, and a lightweight `<DynamicIcon>` component for dynamic vscode-icons.
6
+
7
+ ## Scope
8
+
9
+ Remove `astro-icon` entirely. Use `unplugin-icons` for the 5 static icon usages. Build a small `<DynamicIcon>` component (reading from `@iconify-json/vscode-icons/icons.json`) for the 2 dynamic vscode-icons usages.
10
+
11
+ ## Approach
12
+
13
+ ### 1. Configuration
14
+
15
+ - **`package.json`**: Remove `"astro-icon"` dependency.
16
+ - **`astro.config.mjs`**: Remove `import icon from "astro-icon"`, the `icon()` integration, and `vite.optimizeDeps.include: ['astro-icon']`. Add `unplugin-icons/astro` integration.
17
+
18
+ ### 2. Static Icons → unplugin-icons
19
+
20
+ 5 usages become direct imports:
21
+
22
+ | File | Icon | Import |
23
+ | --------------------- | ------------------------------ | --------------------------------------------------------------------- |
24
+ | `Header.astro` | material-symbols:sunny-rounded | `import MdiSunnyRounded from "~icons/material-symbols/sunny-rounded"` |
25
+ | `Header.astro` | material-symbols:moon-stars | `import MdiMoonStars from "~icons/material-symbols/moon-stars"` |
26
+ | `cv/index.astro` | mdi:external-link | `import MdiExternalLink from "~icons/mdi/external-link"` |
27
+ | `RecursiveList.astro` | mdi:folder | `import MdiFolder from "~icons/mdi/folder"` |
28
+ | `RepoLayout.astro` | mdi:information-outline | `import MdiInformationOutline from "~icons/mdi/information-outline"` |
29
+
30
+ Prop mapping: `size="Npx"` → `width={N} height={N}`, `color` passes through, `style` passes through as a style attribute.
31
+
32
+ ### 3. Dynamic vscode-icons → `<DynamicIcon>` component
33
+
34
+ New file `src/components/DynamicIcon.astro`:
35
+
36
+ - Reads from `@iconify-json/vscode-icons/icons.json` (already imported for fallback checks)
37
+ - Props: `name` (icon key), `size` (default `24px`), `color` (default `currentColor`)
38
+ - Renders the SVG inline from the iconify JSON data
39
+ - Falls back to `default-file` on missing icon
40
+ - Removes the duplicate `import allIcons` + fallback logic from `RecursiveList.astro` and `FileLayout.astro`
41
+
42
+ ## Files Changed
43
+
44
+ - `package.json` — remove `astro-icon`, add `unplugin-icons`
45
+ - `astro.config.mjs` — remove astro-icon integration, add unplugin-icons
46
+ - `src/components/Header.astro` — swap Icon imports
47
+ - `src/components/RecursiveList.astro` — swap folder icon, replace dynamic icon
48
+ - `src/layouts/RepoLayout.astro` — swap info icon
49
+ - `src/layouts/FileLayout.astro` — replace dynamic icon
50
+ - `src/pages/cv/index.astro` — swap external link icon
51
+ - `src/components/DynamicIcon.astro` — new component (created)
package.json CHANGED
@@ -1,41 +1,59 @@
1
1
  {
2
2
  "name": "website",
3
+ "version": "0.0.1",
3
4
  "private": true,
4
5
  "type": "module",
5
- "version": "0.0.1",
6
- "engines": {
7
- "bun": "1.3.3"
8
- },
9
6
  "scripts": {
10
7
  "dev": "astro dev",
11
8
  "build": "astro build",
12
9
  "build:resume:pdf": "typst compile src/assets/pdfs/resume.typ -f pdf src/assets/pdfs/resume.pdf",
13
10
  "build:resume:svg": "typst compile src/assets/pdfs/resume.typ -f svg src/assets/pdfs/resume.svg",
11
+ "cf-typegen": "wrangler types",
12
+ "deploy": "bun run build:resume:pdf && bun run build:resume:svg && astro build && rclone sync -P repos r2:pyrossh-repos-prd && wrangler deploy --config dist/server/wrangler.json",
14
- "deploy": "alchemy deploy --stage prd"
13
+ "prepare": "vp config"
15
14
  },
16
15
  "dependencies": {
17
16
  "@appzic/astro-reset-css": "^1.2.0",
18
17
  "@astrojs/cloudflare": "^13.5.0",
19
- "alchemy": "^0.93.7",
20
18
  "astro": "6.3.1",
21
19
  "astro-color-scheme": "^1.1.6",
22
20
  "astro-expressive-code": "^0.42.0",
23
21
  "astro-icon": "^1.1.5",
22
+ "diff": "^9.0.0",
23
+ "isomorphic-git": "^1.38.6",
24
+ "marked": "^18.0.5",
24
25
  "pretty-bytes": "^7.1.0",
25
- "simple-git": "^3.27.0",
26
26
  "timeago.js": "^4.0.2"
27
27
  },
28
28
  "devDependencies": {
29
+ "@astrojs/check": "^0.9.9",
29
30
  "@astrojs/rss": "^4.0.18",
30
31
  "@astrojs/sitemap": "^3.7.2",
31
- "@aws-sdk/client-s3": "^3.932.0",
32
- "@aws-sdk/client-sts": "^3.932.0",
33
32
  "@iconify-json/material-symbols": "^1.2.39",
34
33
  "@iconify-json/mdi": "^1.2.3",
35
34
  "@iconify-json/vscode-icons": "^1.2.36",
36
35
  "@playwright/test": "^1.52.0",
37
36
  "@types/bun": "^1.3.2",
38
37
  "diff2html": "^3.4.52",
38
+ "typescript": "^6.0.3",
39
+ "unplugin-icons": "^23.0.1",
40
+ "vite": "catalog:",
41
+ "vite-plus": "catalog:",
39
- "vscode-icons": "https://github.com/vscode-icons/vscode-icons.git"
42
+ "vscode-icons": "https://github.com/vscode-icons/vscode-icons.git",
43
+ "wrangler": "^4.105.0"
44
+ },
45
+ "overrides": {
46
+ "@iconify/tools": "5.0.5",
47
+ "@iconify/utils": "3.1.0"
48
+ },
49
+ "devEngines": {
50
+ "packageManager": {
51
+ "name": "pnpm",
52
+ "version": "11.9.0",
53
+ "onFail": "download"
54
+ }
55
+ },
56
+ "engines": {
57
+ "bun": "1.3.3"
40
58
  }
41
- }
59
+ }