Skip to content

Commit 7200527

Browse files
committed
fix(generator): replace & with & in CSS @import font URLs inside SVG style blocks
1 parent e9da96e commit 7200527

3 files changed

Lines changed: 14 additions & 14 deletions

File tree

app/api/streak/route.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,7 @@ describe('GET /api/streak', () => {
826826
expect(response.status).toBe(200);
827827
expect(body).toContain('Space Grotesk');
828828
// Whitespace-only should not produce a Google Fonts import with an empty family
829-
expect(body).not.toContain('family=+&display=swap');
829+
expect(body).not.toContain('family=+&display=swap');
830830
});
831831

832832
it('passes a valid predefined font name through to the SVG', async () => {
@@ -861,7 +861,7 @@ describe('GET /api/streak', () => {
861861
expect(response.status).toBe(200);
862862
expect(body).toContain('Space Grotesk');
863863
// No empty Google Fonts import should be emitted
864-
expect(body).not.toContain('family=&display=swap');
864+
expect(body).not.toContain('family=&display=swap');
865865
});
866866

867867
it('strips dangerous characters from a font name containing a double-quote', async () => {
@@ -919,7 +919,7 @@ describe('GET /api/streak', () => {
919919
expect(response.status).toBe(200);
920920
expect(body).toContain('Fira Code');
921921
// Should NOT emit a second dynamic import for the same font
922-
expect(body).not.toContain('family=fira&display=swap');
922+
expect(body).not.toContain('family=fira&display=swap');
923923
});
924924

925925
it('returns 200 and a valid SVG even when an extreme font value is supplied', async () => {

lib/svg/generator.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ describe('generateSVG', () => {
141141
);
142142

143143
expect(svg).toContain(
144-
"@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');"
144+
"@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');"
145145
);
146146
expect(svg).toContain('font-family: "Inter", sans-serif;');
147147
});
@@ -182,7 +182,7 @@ describe('generateSVG', () => {
182182
mockCalendar
183183
);
184184
// Should NOT contain a dynamic google fonts import for an empty/invalid family
185-
expect(svg).not.toContain('family=&display=swap');
185+
expect(svg).not.toContain('family=&display=swap');
186186
// Should use default body font
187187
expect(svg).toContain('font-family: "Space Grotesk", sans-serif');
188188
});
@@ -194,7 +194,7 @@ describe('generateSVG', () => {
194194
mockCalendar
195195
);
196196
expect(svg).toContain('Space Grotesk');
197-
expect(svg).not.toContain('family=&display=swap');
197+
expect(svg).not.toContain('family=&display=swap');
198198
});
199199

200200
it('uses default font when font param is whitespace only', () => {
@@ -204,7 +204,7 @@ describe('generateSVG', () => {
204204
mockCalendar
205205
);
206206
expect(svg).toContain('Space Grotesk');
207-
expect(svg).not.toContain('family=+&display=swap');
207+
expect(svg).not.toContain('family=+&display=swap');
208208
});
209209

210210
it('allows apostrophes in font names like Times New Roman', () => {

lib/svg/generator.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ function renderStyle(
153153
const fs = (n: number) => Math.round(n * sf * 10) / 10;
154154
return `
155155
<style>
156-
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&amp;family=JetBrains+Mono&amp;family=Roboto&amp;family=Syncopate:wght@400;700&amp;family=Space+Grotesk:wght@400;500;600;700&amp;display=swap');
156+
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&family=JetBrains+Mono&family=Roboto&family=Syncopate:wght@400;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');
157157
${googleFontsImport}
158158
${TOWER_ANIMATION_CSS}
159159
.scan-line {
@@ -338,7 +338,7 @@ export function generateSVG(
338338
const googleFontUrlPart =
339339
sanitizedFont && !isPredefinedFont ? sanitizeGoogleFontUrl(sanitizedFont) : null;
340340
const googleFontsImport = googleFontUrlPart
341-
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&amp;display=swap');`
341+
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&display=swap');`
342342
: '';
343343

344344
const sf = getSizeScale(params.size);
@@ -424,7 +424,7 @@ function generateAutoThemeSVG(
424424
${renderHeader(safeUser, stats, sf, params)}
425425
426426
<style>
427-
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&amp;family=JetBrains+Mono&amp;family=Roboto&amp;family=Syncopate:wght@400;700&amp;family=Space+Grotesk:wght@400;500;600;700&amp;display=swap');
427+
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&family=JetBrains+Mono&family=Roboto&family=Syncopate:wght@400;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');
428428
:root { --cp-bg: #${light.bg}; --cp-text: #${light.text}; --cp-accent: #${light.accent}; }
429429
@media (prefers-color-scheme: dark) { :root { --cp-bg: #${dark.bg}; --cp-text: #${dark.text}; --cp-accent: #${dark.accent}; } }
430430
.cp-bg-fill { fill: var(--cp-bg); } .cp-text-fill { fill: var(--cp-text); color: var(--cp-text); } .cp-accent-fill { fill: var(--cp-accent); color: var(--cp-accent); }
@@ -511,7 +511,7 @@ export function generateMonthlySVG(stats: MonthlyStats, params: BadgeParams): st
511511
const googleFontUrlPart =
512512
sanitizedFont && !isPredefinedFont ? sanitizeGoogleFontUrl(sanitizedFont) : null;
513513
const googleFontsImport = googleFontUrlPart
514-
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&amp;display=swap');`
514+
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&display=swap');`
515515
: '';
516516

517517
const commitsLabel = params.mode === 'loc' ? 'LINES THIS MONTH' : labels.COMMITS_THIS_MONTH;
@@ -557,7 +557,7 @@ export function generateMonthlySVG(stats: MonthlyStats, params: BadgeParams): st
557557
>
558558
<title>Monthly Stats for ${safeUser}</title>
559559
<style>
560-
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&amp;family=JetBrains+Mono&amp;family=Roboto&amp;family=Syncopate:wght@400;700&amp;family=Space+Grotesk:wght@400;500;600;700&amp;display=swap');
560+
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&family=JetBrains+Mono&family=Roboto&family=Syncopate:wght@400;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');
561561
${googleFontsImport}
562562
563563
.title { font-family: ${selectedFont || '"Syncopate", sans-serif'}; fill: ${text}; font-size: 14px; letter-spacing: 2px; font-weight: 400; opacity: 0.8; }
@@ -647,7 +647,7 @@ function generateAutoThemeMonthlySVG(stats: MonthlyStats, params: BadgeParams):
647647
>
648648
<title>Monthly Stats for ${safeUser}</title>
649649
<style>
650-
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&amp;family=JetBrains+Mono&amp;family=Roboto&amp;family=Syncopate:wght@400;700&amp;family=Space+Grotesk:wght@400;500;600;700&amp;display=swap');
650+
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&family=JetBrains+Mono&family=Roboto&family=Syncopate:wght@400;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');
651651
:root { --cp-bg: #${light.bg}; --cp-text: #${light.text}; --cp-accent: #${light.accent}; --cp-negative: #ff4444; }
652652
@media (prefers-color-scheme: dark) { :root { --cp-bg: #${dark.bg}; --cp-text: #${dark.text}; --cp-accent: #${dark.accent}; --cp-negative: #ff6666; } }
653653
.cp-bg-fill { fill: var(--cp-bg); }
@@ -814,7 +814,7 @@ export function generateNotFoundSVG(
814814
</defs>
815815
816816
<style>
817-
@import url('https://fonts.googleapis.com/css2?family=Syncopate:wght@400;700&amp;family=Space+Grotesk:wght@400;500;600&amp;display=swap');
817+
@import url('https://fonts.googleapis.com/css2?family=Syncopate:wght@400;700&family=Space+Grotesk:wght@400;500;600&display=swap');
818818
.title { font-family: "Syncopate", sans-serif; fill: ${text}; font-size: 18px; letter-spacing: 6px; font-weight: 400; opacity: 0.5; }
819819
.label { font-family: "Roboto", sans-serif; fill: ${accent}; font-size: 11px; letter-spacing: 2px; opacity: 0.4; }
820820
.stats { font-family: "Space Grotesk", sans-serif; fill: ${text}; font-size: 42px; font-weight: 500; opacity: 0.2; }

0 commit comments

Comments
 (0)