Skip to content

Commit

Permalink
perf(website): generate css locally and cache loaders (#927)
Browse files Browse the repository at this point in the history
* perf: quick cache documents in cdn

* feat: generate CSS on server instead of relying on cdn

* perf: cache css

* perf: prefetch and cache loaders
  • Loading branch information
ayuhito committed Dec 24, 2023
1 parent 763654a commit cd47292
Show file tree
Hide file tree
Showing 14 changed files with 207 additions and 70 deletions.
4 changes: 3 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 46 additions & 30 deletions website/app/components/preview/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import {
useComputedColorScheme,
} from '@mantine/core';
import { useFocusWithin } from '@mantine/hooks';
import { Fragment, useEffect } from 'react';
import { useEffect } from 'react';

import { useIsFontLoaded } from '@/hooks/useIsFontLoaded';
import { getPreviewText } from '@/utils/language/language';
import type { Metadata } from '@/utils/types';

import { fontVariation, previewState } from './observables';
Expand All @@ -23,13 +24,13 @@ interface TagProps {
interface TextBoxProps {
family: string;
weight: number;
loaded: boolean;
style: string;
}

interface TextAreaProps {
metadata: Metadata;
variableCssKey?: string;
previewText: string;
staticCSS: string;
variableCSS?: string;
}

const Tag = ({ weight, active }: TagProps) => {
Expand All @@ -55,7 +56,7 @@ const Tag = ({ weight, active }: TagProps) => {
);
};

const TextBox = ({ family, weight, loaded }: TextBoxProps) => {
const TextBox = ({ family, weight, style }: TextBoxProps) => {
const { ref, focused } = useFocusWithin();
const state = useSelector(previewState);
const variation = useSelector(fontVariation);
Expand All @@ -67,12 +68,15 @@ const TextBox = ({ family, weight, loaded }: TextBoxProps) => {
: previewState.color.set('#000000');
}, [colorScheme]);

const isFontLoaded = useIsFontLoaded(family, { weights: [weight], style });

return (
<>
<Skeleton visible={loaded}>
<Box className={classes['text-wrapper']}>
<Box className={classes['text-wrapper']}>
<Skeleton visible={!isFontLoaded}>
<TextInput
variant="unstyled"
className={classes.text}
styles={{
input: {
fontFamily: `"${family}", "Fallback Outline"`,
Expand All @@ -94,49 +98,61 @@ const TextBox = ({ family, weight, loaded }: TextBoxProps) => {
autoComplete="off"
ref={ref}
/>
</Box>
</Skeleton>
</Skeleton>
</Box>
<Tag weight={weight} active={focused} />
</>
);
};

const TextArea = ({ metadata, variableCssKey }: TextAreaProps) => {
const { id, family, weights, variable } = metadata;

const variableFamily = `${family} Variable`;
const TextArea = ({ metadata, staticCSS, variableCSS }: TextAreaProps) => {
const { id, family, weights, variable, defSubset, category } = metadata;
const isVariable = Boolean(variable);

const isFontLoaded = useIsFontLoaded(isVariable ? variableFamily : family);
const isItal = useSelector(previewState.italic);
const style = isItal ? 'italic' : 'normal';

const isNotLatin =
defSubset !== 'latin' || category === 'icons' || category === 'other';
useEffect(() => {
if (isNotLatin) {
previewState.text.set(getPreviewText(defSubset, id));
}
}, [isNotLatin, defSubset, id]);

return (
<Flex direction="column">
<Text className={classes.header}>Font Preview</Text>
{!isVariable && (
<style
dangerouslySetInnerHTML={{
__html: staticCSS,
}}
/>
)}
{!isVariable &&
weights.map((weight) => (
<Fragment key={`s-${weight}`}>
<link
rel="stylesheet"
href={`https://cdn.jsdelivr.net/fontsource/css/${id}@latest/${weight}.css`}
/>
<TextBox weight={weight} family={family} loaded={!isFontLoaded} />
</Fragment>
<TextBox
key={`s-${weight}-${style}`}
family={family}
weight={weight}
style={style}
/>
))}
{isVariable && (
<link
rel="stylesheet"
href={`https://cdn.jsdelivr.net/fontsource/css/${id}:vf@latest/${
variableCssKey ?? 'wght'
}.css`}
{isVariable && variableCSS && (
<style
dangerouslySetInnerHTML={{
__html: variableCSS,
}}
/>
)}
{isVariable &&
weights.map((weight) => (
<TextBox
key={`v-${weight}`}
key={`v-${weight}-${style}`}
family={`${family} Variable`}
weight={weight}
family={variableFamily}
loaded={!isFontLoaded}
style={style}
/>
))}
</Flex>
Expand Down
6 changes: 4 additions & 2 deletions website/app/components/search/Hits.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useSelector } from '@legendapp/state/react';
import { Box, Group, SimpleGrid, Skeleton, Text } from '@mantine/core';
import { Link as NavLink } from '@remix-run/react';
import { useEffect, useRef, useState } from 'react';
import { useInfiniteHits, useInstantSearch } from 'react-instantsearch';

Expand Down Expand Up @@ -47,8 +48,9 @@ const HitComponent = ({ hit, fontSize }: HitComponentProps) => {

return (
<Box
component="a"
href={`/fonts/${hit.objectID}`}
renderRoot={({ ...others }) => (
<NavLink prefetch="intent" to={`/fonts/${hit.objectID}`} {...others} />
)}
className={classes.wrapper}
mih={{ base: '150px', sm: displaySelect === 'grid' ? '332px' : '150px' }}
>
Expand Down
22 changes: 13 additions & 9 deletions website/app/hooks/useIsFontLoaded.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
// @ts-expect-error - The type definition is wrong
import useFontFaceObserver from 'use-font-face-observer';

export const useIsFontLoaded = (family: string, weights?: number[]) => {
const isFontLoaded = useFontFaceObserver(
[
interface ObserverOptions {
weights?: number[];
style?: string;
}

export const useIsFontLoaded = (family: string, options?: ObserverOptions) => {
if (!options?.weights || options.weights.length === 0)
return useFontFaceObserver(
[{ family, style: options?.style ?? 'normal' }],
{
family,
timeout: 15_000,
},
],
{ timeout: 15_000 },
);
if (!weights || weights.length === 0) return isFontLoaded;
);

const loadingArray = weights.map((weight) => {
const loadingArray = options?.weights.map((weight) => {
// Loop loading order is guaranteed to be consistent, so we can disable the rule
// eslint-disable-next-line react-hooks/rules-of-hooks
return useFontFaceObserver(
[
{
family,
weight: String(weight),
style: options.style ?? 'normal',
},
],
{ timeout: 15_000 },
Expand Down
4 changes: 2 additions & 2 deletions website/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import '@fontsource-variable/inter/wght.css';
import '@fontsource-variable/source-code-pro/wght.css';
import 'fallback-font/fallback-outline.css';
import '@mantine/core/styles.css';
import './styles/global.css';
import '@/styles/global.css';

import { enableLegendStateReact } from '@legendapp/state/react';
import { ColorSchemeScript, MantineProvider } from '@mantine/core';
Expand Down Expand Up @@ -32,7 +32,7 @@ export const meta: MetaFunction = () => {
};

export const headers: HeadersFunction = () => ({
'Accept-CH': 'Sec-CH-Prefers-Color-Scheme',
'Cache-Control': 'public, s-maxage=60',
});

export const links: LinksFunction = () => [
Expand Down
13 changes: 7 additions & 6 deletions website/app/routes/[robots.txt].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ Allow: /
Sitemap: https://fontsource.org/sitemap.xml`;

const headers = {
'Content-Type': 'text/plain',
'Cache-Control': 'public, max-age=86400', // 1 day
};

if (process.env.FLY_APP_NAME === 'fontsource') {
return new Response(prod, {
headers: {
'Content-Type': 'text/plain',
},
headers,
});
}

const dev = `User-agent: *
Disallow: /`;

return new Response(dev, {
headers: {
'Content-Type': 'text/plain',
},
headers,
});
};
1 change: 1 addition & 0 deletions website/app/routes/[sitemap.xml].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const loader: LoaderFunction = async () => {
return new Response(sitemap, {
headers: {
'Content-Type': 'application/xml',
'Cache-Control': 'public, max-age=86400', // 1 day
},
});
};
15 changes: 11 additions & 4 deletions website/app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,17 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
// Add server state to local cache before responding
setSSRCache(serverUrl, serverState);

return json<SearchProps>({
serverState,
serverUrl,
});
return json<SearchProps>(
{
serverState,
serverUrl,
},
{
headers: {
'Cache-Control': 'public, max-age=300',
},
},
);
};

export default function Index() {
Expand Down
9 changes: 8 additions & 1 deletion website/app/routes/docs.$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
throw new Response('Not found', { status: 404 });
}

return json<LoaderData>({ code: mdx.code, frontmatter: mdx.frontmatter });
return json<LoaderData>(
{ code: mdx.code, frontmatter: mdx.frontmatter },
{
headers: {
'Cache-Control': 'public, max-age=300',
},
},
);
};

export const meta: MetaFunction<typeof loader> = ({ data }) => {
Expand Down

0 comments on commit cd47292

Please sign in to comment.