Skip to content

Commit 0e9f602

Browse files
committed
googlefonts configuration option (**experimental and temporary**)
ref. #423
1 parent 329d89c commit 0e9f602

File tree

7 files changed

+32
-8
lines changed

7 files changed

+32
-8
lines changed

docs/config.md

+8
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,11 @@ The set of replacements for straight double and single quotes used when the [**t
300300
## linkify <a href="https://github.com/observablehq/framework/releases/tag/v1.7.0" class="observablehq-version-badge" data-version="^1.7.0" title="Added in 1.7.0"></a>
301301

302302
If true (the default), automatically convert URL-like text to links in Markdown.
303+
304+
<!--
305+
306+
## googlefonts <a href="https://github.com/observablehq/framework/pull/1588" class="observablehq-version-badge" data-version="prerelease" title="Added in #1588"></a>
307+
308+
The URL of a Google Fonts resource. This is a **temporary** configuration element, which will be superseded when fonts are self-hosted (see [#423](https://github.com/observablehq/framework/issues/423)).
309+
310+
-->

observablehq.config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export default {
1515
root: "docs",
1616
output: "docs/.observablehq/dist",
1717
title: "Observable Framework",
18+
googlefonts:
19+
"https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Spline+Sans+Mono:ital,wght@0,300..700;1,300..700&display=swap",
1820
pages: [
1921
{name: "What is Framework?", path: "/what-is-framework"},
2022
{name: "Getting started", path: "/getting-started"},

src/build.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export async function build(
4949
{config}: BuildOptions,
5050
effects: BuildEffects = new FileBuildEffects(config.output, join(config.root, ".observablehq", "cache"))
5151
): Promise<void> {
52-
const {root, loaders, normalizePath} = config;
52+
const {root, loaders, normalizePath, googlefonts} = config;
5353
Telemetry.record({event: "build", step: "start"});
5454

5555
// Make sure all files are readable before starting to write output files.
@@ -79,7 +79,7 @@ export async function build(
7979
effects.logger.log(faint("(skipped)"));
8080
continue;
8181
}
82-
const resolvers = await getResolvers(page, {root, path: sourceFile, normalizePath, loaders});
82+
const resolvers = await getResolvers(page, {root, path: sourceFile, normalizePath, loaders, googlefonts});
8383
const elapsed = Math.floor(performance.now() - start);
8484
for (const f of resolvers.assets) files.add(resolvePath(sourceFile, f));
8585
for (const f of resolvers.files) files.add(resolvePath(sourceFile, f));

src/config.ts

+9
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export interface Config {
8484
head: PageFragmentFunction | string | null; // defaults to null
8585
header: PageFragmentFunction | string | null; // defaults to null
8686
footer: PageFragmentFunction | string | null; // defaults to “Built with Observable on [date].”
87+
googlefonts: string | null; // defaults to Source Pro URL
8788
toc: TableOfContents;
8889
style: null | Style; // defaults to {theme: ["light", "dark"]}
8990
search: SearchConfig | null; // default to null
@@ -105,6 +106,7 @@ export interface ConfigSpec {
105106
head?: unknown;
106107
header?: unknown;
107108
footer?: unknown;
109+
googlefonts?: unknown;
108110
interpreters?: unknown;
109111
title?: unknown;
110112
pages?: unknown;
@@ -239,6 +241,12 @@ export function normalizeConfig(spec: ConfigSpec = {}, defaultRoot?: string, wat
239241
const head = pageFragment(spec.head === undefined ? "" : spec.head);
240242
const header = pageFragment(spec.header === undefined ? "" : spec.header);
241243
const footer = pageFragment(spec.footer === undefined ? defaultFooter() : spec.footer);
244+
const googlefonts =
245+
spec.googlefonts === undefined
246+
? "https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&display=swap"
247+
: spec.googlefonts === null
248+
? null
249+
: String(spec.googlefonts);
242250
const search = spec.search == null || spec.search === false ? null : normalizeSearch(spec.search as any);
243251
const interpreters = normalizeInterpreters(spec.interpreters as any);
244252
const config: Config = {
@@ -253,6 +261,7 @@ export function normalizeConfig(spec: ConfigSpec = {}, defaultRoot?: string, wat
253261
head,
254262
header,
255263
footer,
264+
googlefonts,
256265
toc,
257266
style,
258267
search,

src/preview.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ function handleWatch(socket: WebSocket, req: IncomingMessage, configPromise: Pro
305305

306306
async function watcher(event: WatchEventType, force = false) {
307307
if (!path || !config) throw new Error("not initialized");
308-
const {root, loaders, normalizePath} = config;
308+
const {root, loaders, normalizePath, googlefonts} = config;
309309
switch (event) {
310310
case "rename": {
311311
markdownWatcher?.close();
@@ -336,7 +336,7 @@ function handleWatch(socket: WebSocket, req: IncomingMessage, configPromise: Pro
336336
clearTimeout(emptyTimeout);
337337
emptyTimeout = null;
338338
}
339-
const resolvers = await getResolvers(page, {root, path, loaders, normalizePath});
339+
const resolvers = await getResolvers(page, {root, path, loaders, normalizePath, googlefonts});
340340
if (hash === resolvers.hash) break;
341341
const previousHash = hash!;
342342
const previousHtml = html!;
@@ -373,10 +373,10 @@ function handleWatch(socket: WebSocket, req: IncomingMessage, configPromise: Pro
373373
if (path.endsWith("/")) path += "index";
374374
path = join(dirname(path), basename(path, ".html") + ".md");
375375
config = await configPromise;
376-
const {root, loaders, normalizePath} = config;
376+
const {root, loaders, normalizePath, googlefonts} = config;
377377
const source = await readFile(join(root, path), "utf8");
378378
const page = parseMarkdown(source, {path, ...config});
379-
const resolvers = await getResolvers(page, {root, path, loaders, normalizePath});
379+
const resolvers = await getResolvers(page, {root, path, loaders, normalizePath, googlefonts});
380380
if (resolvers.hash !== initialHash) return void send({type: "reload"});
381381
hash = resolvers.hash;
382382
html = getHtml(page, resolvers);

src/resolvers.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface ResolversConfig {
3333
path: string;
3434
normalizePath: (path: string) => string;
3535
loaders: LoaderResolver;
36+
googlefonts: string | null;
3637
}
3738

3839
const defaultImports = [
@@ -83,7 +84,7 @@ export const builtins = new Map<string, string>([
8384
*/
8485
export async function getResolvers(
8586
page: MarkdownPage,
86-
{root, path, normalizePath, loaders}: ResolversConfig
87+
{root, path, normalizePath, loaders, googlefonts}: ResolversConfig
8788
): Promise<Resolvers> {
8889
const hash = createHash("sha256").update(page.body).update(JSON.stringify(page.data));
8990
const assets = new Set<string>();
@@ -107,7 +108,7 @@ export async function getResolvers(
107108

108109
// Add stylesheets. TODO Instead of hard-coding Source Serif Pro, parse the
109110
// page’s stylesheet to look for external imports.
110-
stylesheets.add("https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&display=swap"); // prettier-ignore
111+
if (googlefonts) stylesheets.add(googlefonts);
111112
if (page.style) stylesheets.add(page.style);
112113

113114
// Collect directly-attached files, local imports, and static imports.

test/config-test.ts

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ describe("readConfig(undefined, root)", () => {
3838
header: "",
3939
footer:
4040
'Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="2024-01-10T16:00:00">Jan 10, 2024</a>.',
41+
googlefonts:
42+
"https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&display=swap",
4143
search: null,
4244
watchPath: resolve("test/input/build/config/observablehq.config.js")
4345
});
@@ -62,6 +64,8 @@ describe("readConfig(undefined, root)", () => {
6264
header: "",
6365
footer:
6466
'Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="2024-01-10T16:00:00">Jan 10, 2024</a>.',
67+
googlefonts:
68+
"https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&display=swap",
6569
search: null,
6670
watchPath: undefined
6771
});

0 commit comments

Comments
 (0)