-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(webperf): replace google fonts by local
- Loading branch information
Showing
23 changed files
with
322 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,6 @@ reports | |
coverage | ||
storybook-static | ||
public/data | ||
public/fonts | ||
public/imgs | ||
public/feed.xml | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
FROM node:18.16-alpine3.16 | ||
|
||
# Install Python | ||
RUN apk add --no-cache python3 | ||
|
||
# Update package and install necessary build tools | ||
RUN apk update && apk add --no-cache build-base python3 py3-pip | ||
|
||
# Upgrade setuptools | ||
RUN pip install -U setuptools | ||
|
||
# Install brotli and fonttools | ||
RUN pip install brotli fonttools | ||
|
||
# Install glyphhanger | ||
RUN npm install -g glyphhanger tsx | ||
|
||
WORKDIR /var/www/app | ||
|
||
USER node:node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#!/usr/bin/env node | ||
|
||
import chalk from 'chalk'; | ||
import { exec } from 'node:child_process'; | ||
import { statSync } from 'node:fs'; | ||
import { resolve } from 'node:path'; | ||
|
||
import { fonts, subsets } from '../src/config/website/fonts'; | ||
|
||
// Define the directory for fonts | ||
const fontsDir = resolve(process.cwd(), 'src/assets/fonts'); | ||
const fontsOutdir = resolve(process.cwd(), 'public/fonts'); | ||
|
||
const formatUnicode = (unicode: string): string => unicode.padStart(4, '0').toUpperCase(); | ||
const formatUnicodeFromNumber = (unicodeNumber: number, includePrefix: boolean = true): string => { | ||
const formattedUnicode = formatUnicode(unicodeNumber.toString(16)); | ||
|
||
return includePrefix ? `U+${formattedUnicode}` : formattedUnicode; | ||
}; | ||
|
||
const getUnicodeTableByUnicodeRange = ( | ||
unicodeRangeString: string | ||
): { range: string; codePoints: number[]; unicodes: string[]; characters: string[] }[] => | ||
unicodeRangeString | ||
.replace(/U\+/g, '') | ||
.split(',') | ||
.reduce<{ range: string; codePoints: number[]; unicodes: string[]; characters: string[] }[]>( | ||
(unicodeTable, currentRange) => { | ||
if (currentRange.includes('-')) { | ||
const [start, end] = currentRange.split('-').map((i) => parseInt(i, 16)); | ||
const codePoints: number[] = Array.from({ length: end - start + 1 }, (_, index) => start + index); | ||
|
||
return [ | ||
...unicodeTable, | ||
{ | ||
range: currentRange, | ||
codePoints, | ||
unicodes: codePoints.map((codePoint) => formatUnicodeFromNumber(Number(codePoint))), | ||
characters: codePoints.map((codePoint) => String.fromCharCode(codePoint)), | ||
}, | ||
]; | ||
} | ||
|
||
const codePoint: number = parseInt(currentRange, 16); | ||
|
||
return [ | ||
...unicodeTable, | ||
{ | ||
range: currentRange, | ||
codePoints: [codePoint], | ||
unicodes: [currentRange], | ||
characters: [String.fromCharCode(codePoint)], | ||
}, | ||
]; | ||
}, | ||
[] | ||
); | ||
|
||
// Define supported font formats | ||
const formats: string[] = ['woff2']; | ||
|
||
// Function to get file size in kilobytes | ||
const getFileSizeInBytes = (filePath: string): string => { | ||
const stats = statSync(filePath); | ||
|
||
return `${stats.size / 1000} kB`; | ||
}; | ||
const optimizeFonts = (): void => { | ||
const unicodesInSubsets: string[] = []; | ||
for (const [subset, unicodeRange] of Object.entries(subsets)) { | ||
const unicodeTable = getUnicodeTableByUnicodeRange(unicodeRange); | ||
const unicodes = unicodeTable.reduce<string[]>( | ||
(currentUnicodes, item) => [...currentUnicodes, ...item.unicodes], | ||
[] | ||
); | ||
unicodesInSubsets.push(...unicodes); | ||
console.log( | ||
`Here are the characters you selected in the \`${chalk.blue.bold(subset)}\` subset: ${unicodeTable | ||
.map((item) => item.characters.map((character) => `\`${chalk.yellow(character)}\``)) | ||
.join(', ')}.\n` | ||
); | ||
} | ||
|
||
// Loop through each font configuration | ||
for (const { fontDirectoryName, styles } of fonts) { | ||
// Define source and optimized directories | ||
const sourcesDir = resolve(fontsDir, fontDirectoryName); | ||
|
||
// Loop through each font style | ||
for (const { fontFileName } of styles) { | ||
const sourceFontFileNameWithExtension = `${fontFileName}.ttf`; | ||
const sourceFontPath = resolve(sourcesDir, sourceFontFileNameWithExtension); | ||
const sourceFontFileSize = getFileSizeInBytes(sourceFontPath); | ||
for (const [subset, unicodeRange] of Object.entries(subsets)) { | ||
for (const format of formats) { | ||
const optimizedFontFileName = `${fontFileName}-${subset}.${format}`; | ||
const optimizedFontPath = resolve(fontsOutdir, optimizedFontFileName); | ||
const args = [ | ||
sourceFontPath, | ||
`--output-file="${optimizedFontPath}"`, | ||
`--flavor=${format}`, | ||
'--layout-features="*"', | ||
`--unicodes="${unicodeRange}"`, | ||
]; | ||
exec(`pyftsubset ${args.join(' \\\n')}`, (error): void => { | ||
if (error) { | ||
console.error(`Error: ${error}`); | ||
|
||
return; | ||
} | ||
const optimizedFontFileSize = getFileSizeInBytes(optimizedFontPath); | ||
console.log( | ||
`Subsetting ${chalk.bold(sourceFontFileNameWithExtension)} to ${chalk.bold( | ||
optimizedFontFileName | ||
)} (was ${chalk.red(sourceFontFileSize)}, now ${chalk.green(optimizedFontFileSize)})` | ||
); | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
|
||
optimizeFonts(); |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Define the type for font weight | ||
export type FontWeightType = | ||
| 'thin' | ||
| 'extra-light' | ||
| 'light' | ||
| 'regular' | ||
| 'medium' | ||
| 'semi-bold' | ||
| 'bold' | ||
| 'extra-bold' | ||
| 'black'; | ||
|
||
export const fonts: { | ||
fontFamilyName: string; | ||
fontFamily: string; | ||
fontDirectoryName: string; | ||
styles: { | ||
fontFileName: string; | ||
fontWeight: FontWeightType; | ||
isItalic?: boolean; | ||
isPreload?: boolean; | ||
}[]; | ||
}[] = [ | ||
{ | ||
fontFamilyName: 'Montserrat', | ||
fontFamily: 'Montserrat, helvetica neue, helvetica, arial, sans-serif', | ||
fontDirectoryName: 'montserrat', | ||
styles: [ | ||
{ | ||
fontFileName: 'montserrat-regular', | ||
fontWeight: 'regular', | ||
}, | ||
{ | ||
fontFileName: 'montserrat-medium', | ||
fontWeight: 'medium', | ||
}, | ||
{ | ||
fontFileName: 'montserrat-semi-bold', | ||
fontWeight: 'semi-bold', | ||
}, | ||
], | ||
}, | ||
{ | ||
fontFamilyName: 'Agdasima', | ||
fontFamily: 'Agdasima', | ||
fontDirectoryName: 'agdasima', | ||
styles: [ | ||
{ | ||
fontFileName: 'agdasima-regular', | ||
fontWeight: 'regular', | ||
}, | ||
{ | ||
fontFileName: 'agdasima-bold', | ||
fontWeight: 'bold', | ||
}, | ||
], | ||
}, | ||
]; | ||
|
||
// Define subsets for different languages | ||
// Keep this order, because the one in first position will be used for the preload | ||
export const subsets: Record<string, string> = { | ||
latin: | ||
'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD', | ||
'latin-ext': | ||
'U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { fonts, FontWeightType, subsets } from '@/config/website/fonts'; | ||
import { getPathFile } from '@/helpers/assetHelper'; | ||
|
||
// Function to get numerical font weight based on the FontWeightType | ||
const getFontWeightNumber = (fontWeight: FontWeightType): number => { | ||
// eslint-disable-next-line default-case | ||
switch (fontWeight) { | ||
case 'thin': | ||
return 100; | ||
case 'extra-light': | ||
return 200; | ||
case 'light': | ||
return 300; | ||
case 'regular': | ||
return 400; | ||
case 'medium': | ||
return 500; | ||
case 'semi-bold': | ||
return 600; | ||
case 'bold': | ||
return 700; | ||
case 'extra-bold': | ||
return 800; | ||
case 'black': | ||
return 900; | ||
} | ||
throw new Error(`This fontWeight "${fontWeight}" does not exist`); | ||
}; | ||
|
||
const templateFontFace = (options: { | ||
fontFamilyName: string; | ||
fontPath: string; | ||
fontWeight: FontWeightType; | ||
unicodeRange: string; | ||
isItalic?: boolean; | ||
}): string => | ||
`@font-face { | ||
font-family: '${options.fontFamilyName}'; | ||
font-style: ${options.isItalic ? 'italic' : 'normal'}; | ||
font-weight: ${getFontWeightNumber(options.fontWeight)}; | ||
font-display: swap; | ||
src: url(${getPathFile(options.fontPath)}) format('woff2'); | ||
unicode-range: ${options.unicodeRange}; | ||
}`.replace(/\n|\s+(?!format)/g, ''); | ||
|
||
export const fontFaces = Object.entries(subsets) | ||
.reduce<string[]>((currentFontFaces, [subsetName, unicodeRange]) => { | ||
for (const font of fonts) { | ||
for (const style of font.styles) { | ||
currentFontFaces.push( | ||
templateFontFace({ | ||
fontFamilyName: font.fontFamilyName, | ||
fontPath: `/fonts/${style.fontFileName}-${subsetName}.woff2`, | ||
fontWeight: style.fontWeight, | ||
isItalic: style.isItalic, | ||
unicodeRange, | ||
}) | ||
); | ||
} | ||
} | ||
return currentFontFaces; | ||
}, []) | ||
.join(''); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters