Skip to content

Commit da8e168

Browse files
authored
refactor(tools): svg-converter cleanup, shared tool skeletons, ytm cookie script (#85)
1 parent e3f2fd9 commit da8e168

16 files changed

Lines changed: 779 additions & 118 deletions

File tree

__tests__/features/miscellaneous/svg-converter/utilities/svg-converter.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,48 @@ describe('SVG sanitization', () => {
8181
})
8282
})
8383

84+
describe('SVG auto-fixes', () => {
85+
it('keeps fixed-dimension SVGs valid and reports the fix as a notice', () => {
86+
const item = normalizeSources(
87+
extractSvgs(
88+
'<svg width="24" height="24" viewBox="0 0 24 24"><path d="M0 0"/></svg>'
89+
).sources
90+
)[0]
91+
expect(item.state).toBe('valid')
92+
expect(item.warnings).toEqual([])
93+
expect(item.notices.join(' ')).toContain('size prop')
94+
})
95+
96+
it('derives a viewBox from width and height when missing', () => {
97+
const item = normalizeSources(
98+
extractSvgs('<svg width="16" height="32"><path d="M0 0"/></svg>')
99+
.sources
100+
)[0]
101+
expect(item.state).toBe('valid')
102+
expect(item.viewBox).toBe('0 0 16 32')
103+
expect(generateComponent(item)).toContain('viewBox="0 0 16 32"')
104+
expect(item.notices.join(' ')).toContain('added viewBox')
105+
})
106+
107+
it('still warns when no viewBox can be derived', () => {
108+
const item = normalizeSources(
109+
extractSvgs('<svg><path d="M0 0"/></svg>').sources
110+
)[0]
111+
expect(item.state).toBe('warning')
112+
expect(item.warnings.join(' ')).toContain('no viewBox')
113+
})
114+
115+
it('treats auto-resolved duplicate names as valid with a notice', () => {
116+
const items = normalizeSources(
117+
extractSvgs(
118+
'<svg viewBox="0 0 2 2" data-icon="a"/><svg viewBox="0 0 2 2" data-icon="a"/>'
119+
).sources
120+
)
121+
expect(items[1].state).toBe('valid')
122+
expect(items[1].notices.join(' ')).toContain('duplicate component name')
123+
})
124+
})
125+
84126
describe('React conversion', () => {
85127
it('normalizes names and filenames', () => {
86128
expect(componentName('arrow-left', '')).toBe('ArrowLeftIcon')

scripts/update-ytm-cookie.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
cd "$(dirname "$0")/.."
5+
6+
printf 'Paste the full YTM cookie header and press Enter:\n> '
7+
IFS= read -r COOKIE
8+
9+
if [ -z "$COOKIE" ]; then
10+
echo 'No cookie provided, aborting.' >&2
11+
exit 1
12+
fi
13+
14+
python3 - "$COOKIE" <<'PY'
15+
import sys, re, pathlib
16+
cookie = sys.argv[1].strip()
17+
env = pathlib.Path('.env')
18+
text = env.read_text()
19+
pattern = re.compile(r'^YTM_COOKIE=.*$', re.M)
20+
if pattern.search(text):
21+
text = pattern.sub(lambda _: f'YTM_COOKIE={cookie}', text)
22+
else:
23+
text = text.rstrip('\n') + f'\nYTM_COOKIE={cookie}\n'
24+
env.write_text(text)
25+
print('Updated .env')
26+
PY
27+
28+
vercel env rm YTM_COOKIE production --yes 2>/dev/null || true
29+
vercel env add YTM_COOKIE production --value "$COOKIE" --yes
30+
echo 'Pushed YTM_COOKIE to Vercel production.'
31+
32+
read -r -p 'Redeploy production now? [y/N] ' REPLY
33+
if [ "$REPLY" = 'y' ] || [ "$REPLY" = 'Y' ]; then
34+
vercel redeploy --yes 2>/dev/null || vercel --prod
35+
fi

src/app/(marketing)/packages/[slug]/page.tsx

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -190,33 +190,6 @@ async function PackagePageContent({ params }: Props) {
190190
)}
191191
</div>
192192
</header>
193-
194-
<nav
195-
aria-label="On this page"
196-
className="border-b border-border/60 px-4 py-3 md:px-5"
197-
>
198-
<div className="flex gap-x-4 gap-y-2 overflow-x-auto whitespace-nowrap font-mono text-[11px] text-muted-foreground">
199-
<a
200-
href="#why"
201-
className="transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
202-
>
203-
What it replaces
204-
</a>
205-
<a
206-
href="#quick-start"
207-
className="transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
208-
>
209-
Quick start
210-
</a>
211-
<a
212-
href="#api"
213-
className="transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
214-
>
215-
API examples
216-
</a>
217-
</div>
218-
</nav>
219-
220193
<section
221194
id="why"
222195
aria-labelledby="why-heading"

src/features/miscellaneous/components/tool-renderer.tsx

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,27 @@
33
import nextDynamic from 'next/dynamic'
44
import type { ComponentType } from 'react'
55
import type { TToolSlug } from '../constants/tools'
6-
7-
function ToolSkeleton() {
8-
return (
9-
<div
10-
role="status"
11-
aria-label="Loading tool"
12-
className="flex min-h-[70vh] flex-col gap-3"
13-
>
14-
<div className="h-8 w-64 shrink-0 animate-pulse bg-muted/60" />
15-
<div className="h-28 shrink-0 animate-pulse bg-muted/60" />
16-
<div className="grow animate-pulse bg-muted/60" />
17-
</div>
18-
)
19-
}
6+
import { TOOL_SKELETONS } from './tool-skeletons'
207

218
type TLoader = () => Promise<{ default: ComponentType }>
229

23-
function lazyTool(loader: TLoader, ssr = false) {
24-
return nextDynamic(loader, { ssr, loading: ToolSkeleton })
10+
function lazyTool(slug: TToolSlug, loader: TLoader) {
11+
const Fallback = TOOL_SKELETONS[slug]
12+
return nextDynamic(loader, { ssr: true, loading: () => <Fallback /> })
2513
}
2614

2715
const TOOL_COMPONENTS: Record<TToolSlug, ComponentType> = {
28-
'find-replace': lazyTool(() => import('../find-replace')),
29-
'diff-checker': lazyTool(() => import('../diff-checker'), true),
30-
'link-extractor': lazyTool(() => import('../link-extractor'), true),
31-
'json-tool': lazyTool(() => import('../json-tool'), true),
32-
'svg-converter': lazyTool(() => import('../svg-converter'), true),
33-
hemelsbreed: lazyTool(() => import('../hemelsbreed')),
34-
'coordinate-marker': lazyTool(() => import('../coordinate-marker')),
35-
'my-location': lazyTool(() => import('../my-location')),
36-
'sendable-video': lazyTool(() => import('../sendable-video')),
37-
'gif-to-video': lazyTool(() => import('../gif-to-video')),
38-
'video-to-gif': lazyTool(() => import('../video-to-gif'))
16+
'find-replace': lazyTool('find-replace', () => import('../find-replace')),
17+
'diff-checker': lazyTool('diff-checker', () => import('../diff-checker')),
18+
'link-extractor': lazyTool('link-extractor', () => import('../link-extractor')),
19+
'json-tool': lazyTool('json-tool', () => import('../json-tool')),
20+
'svg-converter': lazyTool('svg-converter', () => import('../svg-converter')),
21+
hemelsbreed: lazyTool('hemelsbreed', () => import('../hemelsbreed')),
22+
'coordinate-marker': lazyTool('coordinate-marker', () => import('../coordinate-marker')),
23+
'my-location': lazyTool('my-location', () => import('../my-location')),
24+
'sendable-video': lazyTool('sendable-video', () => import('../sendable-video')),
25+
'gif-to-video': lazyTool('gif-to-video', () => import('../gif-to-video')),
26+
'video-to-gif': lazyTool('video-to-gif', () => import('../video-to-gif'))
3927
}
4028

4129
type Props = {

0 commit comments

Comments
 (0)