Skip to content

Commit

Permalink
automatic font loading
Browse files Browse the repository at this point in the history
  • Loading branch information
lilnasy committed Nov 15, 2024
1 parent d07b61a commit b5f364a
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 39 deletions.
Binary file removed assets/wXK3E20CsoJ9j1DDkjHcQ5ZL8xRaxru9no1P2w.woff2
Binary file not shown.
13 changes: 5 additions & 8 deletions astro.config.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { fileURLToPath } from "node:url"
import { defineConfig, type ViteUserConfig } from "astro/config"
import node from "@withastro/node"
import preact from "@preact/preset-vite"
import emotion from "astro-emotion"
import precompress from "./precompress.ts"
import precompress from "./lib/precompress.ts"
import fontLoader from "./lib/font-loader.ts"


const vite: ViteUserConfig = {
plugins: [ preact() ],
assetsInclude: "font:*",
plugins: [ preact(), fontLoader() ],
ssr: {
noExternal: import.meta.env.PROD || undefined
},
resolve: {
alias: {
assets: fileURLToPath(new URL("./assets", import.meta.url))
}
},
build: {
assetsInlineLimit: 0,
sourcemap: true,
Expand Down
7 changes: 6 additions & 1 deletion env.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/// <reference path=".astro/types.d.ts" />
/// <reference types="astro/client" />
/// <reference types="astro-emotion/client" />
/// <reference types="astro-emotion/client" />

declare module 'font:*' {
const href: string
export default href
}
83 changes: 83 additions & 0 deletions lib/font-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import path from "node:path"
import crypto from "node:crypto"
import fs from "node:fs"
import type { Plugin } from "vite"

export default function (): Plugin {
let root: string
let metadata: Record<string, string>
function metadataFilePath() {
return path.join(root, "node_modules", ".fonts", "metadata.json")
}
function readMetadata() {
if (metadata) return metadata
const metadataFilePath = path.join(root, "node_modules", ".fonts", "metadata.json")
try {
const metadataText = fs.readFileSync(metadataFilePath, "utf8")
metadata = JSON.parse(metadataText)
} catch {
fs.writeFileSync(metadataFilePath, "{}")
metadata = {}
}
return metadata
}
function writeMetadata() {
fs.writeFileSync(metadataFilePath(), JSON.stringify(metadata, null, 4))
}
const resolveId: Plugin["resolveId"] = async function (id) {
if (id.startsWith("font:") === false && id.startsWith("/font:") === false) {
return
}
const query = id.startsWith("font:") ? id.slice(5) : id.slice(6)
try {
const metadata = readMetadata()
const filePath = metadata[query]
if (filePath) return filePath
}
catch {
// continue
}
const cssResponse = await fetch(`https://fonts.googleapis.com/css2?${query}`, {
headers: {
"User-Agent": "AppleWebKit/537.36 Chrome/130.0.0.0"
}
})
const cssText = await cssResponse.text()
const [ fontPath ] = /(?<=url\()https:\/\/fonts.gstatic.com\/(\S)+(?=\))/.exec(cssText)!
const fontResponse = await fetch(fontPath)
const fontBuffer = await fontResponse.arrayBuffer()
const fontBytes = new Uint8Array(fontBuffer)
const hash = crypto.hash("md5", fontBytes)
const filePath = path.join(root, "node_modules", ".fonts", `${hash}.woff2`)
fs.mkdirSync(path.dirname(filePath))
fs.writeFileSync(filePath, fontBytes)
const metadata = readMetadata()
metadata[query] = filePath
writeMetadata()
return filePath
}
return {
name: "font-loader",
config() {
return {
resolve: {
alias: [{
find: /font:/,
replacement: "font:",
// url() in css can only be hooked into this way,
// and even then the url must start with a forward
// slash - dunno why, just vite being vite
customResolver: { resolveId }
}]
}
}
},
configureServer({ config }) {
root = config.root
},
configResolved(config) {
root = config.root
},
resolveId
}
}
File renamed without changes.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
"devDependencies": {
"@preact/preset-vite": "^2.9.1",
"@withastro/node": "^8.4.0",
"astro": "^4.16.9",
"astro": "^4.16.13",
"astro-emotion": "^2.0.0",
"canvas-confetti": "^1.9.3",
"clsx": "^2.1.1",
"iterator-helpers-polyfill": "^3.0.1",
"preact": "^10.24.3",
"preact-render-to-string": "^6.5.11",
"typescript": "^5.6.3",
"vite": "^5.4.10",
"ws": "^8.18.0"
},
"packageManager": "pnpm@9.12.1",
Expand Down
10 changes: 5 additions & 5 deletions pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import favicon from "./_favicon.svg"
import { h } from "preact"
import { renderToString } from "preact-render-to-string"
import { GameUISSR } from "components/GameUI.tsx"
import font from "assets/wXK3E20CsoJ9j1DDkjHcQ5ZL8xRaxru9no1P2w.woff2"
// import font from "font:family=Sue Ellen Francisco"
import "../tokens.css"
---
<html lang="en">
Expand All @@ -14,14 +14,14 @@ import "../tokens.css"
<title>Tic Tac Toe</title>
<meta name="description" content="Enjoy the classic game of Tic Tac Toe in your browser! Play against friends or use random matchmaking and beat them all to pulp!."/>
<link rel="icon" type="image/svg+xml" href={favicon.src} />
<link rel="preload" as="font" type="font/woff2" href={font} crossorigin/>
<!-- <link rel="preload" as="font" type="font/woff2" href={font} crossorigin/> -->
<style is:global>
@font-face {
font-family: "Sue Ellen Francisco";
font-style: normal;
font-weight: 400;
font-display: block;
src: url(assets/wXK3E20CsoJ9j1DDkjHcQ5ZL8xRaxru9no1P2w.woff2) format('woff2');
src: url("font:family=Sue Ellen Francisco") format('woff2');
unicode-range: 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;
}
body {
Expand Down Expand Up @@ -55,14 +55,14 @@ import "../tokens.css"
}
</style>
<script>
import { h, hydrate } from "preact"
import { h, render } from "preact"
import { GameUI } from "components/GameUI.tsx"
import "components/component-store-reactivity.ts"

if (import.meta.env.DEV) await import("preact/debug")
if ("Iterator" in window === false) await import("iterator-helpers-polyfill")

hydrate(h(GameUI, {}), document.body)
render(h(GameUI, {}), document.body)
</script>
</head>
<body><Fragment set:html={renderToString(h(GameUISSR, {}))}/></body>
Expand Down
51 changes: 27 additions & 24 deletions pnpm-lock.yaml

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

0 comments on commit b5f364a

Please sign in to comment.