observablehq / framework

A static site generator for data apps, dashboards, reports, and more. Observable Framework combines JavaScript on the front-end for interactive graphics with any language on the back-end for data analysis.
https://observablehq.com/framework/
ISC License
2.16k stars 87 forks source link

esbuild crashes when style.css contains @font-face definitions #786

Open Fil opened 4 months ago

Fil commented 4 months ago

following https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face I create a style.css file with:

@font-face {
  font-family: "Trickster";
  src:
    local("Trickster"),
    url("trickster-COLRv1.otf") format("opentype") tech(color-COLRv1),
    url("trickster-outline.otf") format("opentype"),
    url("trickster-outline.woff") format("woff");
}

the css bundling crashes with recommendations to "mark paths as external" (unhelpful, since this is commandeered by framework, not by the user), reference:

style docs/style.css → ✘ [ERROR] Could not resolve "trickster-COLRv1.otf"

    docs/style.css:5:8:
      5 │     url("trickster-COLRv1.otf") format("opentype") tech(color-COLRv1),
        ╵         ~~~~~~~~~~~~~~~~~~~~~~

  You can mark the path "trickster-COLRv1.otf" as external to exclude it from
  the bundle, which will remove this error and leave the unresolved path in the
  bundle.

✘ [ERROR] Could not resolve "trickster-outline.otf"

    docs/style.css:6:8:
      6 │     url("trickster-outline.otf") format("opentype"),
        ╵         ~~~~~~~~~~~~~~~~~~~~~~~

  You can mark the path "trickster-outline.otf" as external to exclude it from
  the bundle, which will remove this error and leave the unresolved path in the
  bundle.

✘ [ERROR] Could not resolve "trickster-outline.woff"

    docs/style.css:7:8:
      7 │     url("trickster-outline.woff") format("woff");
        ╵         ~~~~~~~~~~~~~~~~~~~~~~~~

  You can mark the path "trickster-outline.woff" as external to exclude it from
  the bundle, which will remove this error and leave the unresolved path in the
  bundle.

Unexpected error: Build failed with 3 errors:
docs/style.css:5:8: ERROR: Could not resolve "trickster-COLRv1.otf"
docs/style.css:6:8: ERROR: Could not resolve "trickster-outline.otf"
docs/style.css:7:8: ERROR: Could not resolve "trickster-outline.woff"
Fil commented 4 months ago

Here's a way to fix this:

--- a/src/rollup.ts
+++ b/src/rollup.ts
@@ -25,6 +25,7 @@ function rewriteInputsNamespace(code: string) {
 export async function bundleStyles({path, theme}: {path?: string; theme?: string[]}): Promise<string> {
   const result = await build({
     bundle: true,
+    loader: {".svg": "dataurl", ".eot": "dataurl", ".otf": "dataurl", ".woff": "dataurl"},
     ...(path ? {entryPoints: [path]} : {stdin: {contents: renderTheme(theme!), loader: "css"}}),
     write: false,
     alias: STYLE_MODULES

I don't know enough yet about esbuild: do we really have to explicitly list all the resource types that we may want to inline as css data-urls? If so, which ones should we include? (above, I've included 3 typical font formats + svg; this probably needs more image formats (at least png?), woff2, ttf…).

Another method would be to add these references to _import/ but it seems like a tall order.

Fil commented 4 months ago

While I'm in this section… shouldn't we minify on build? → this point is discussed in #787

mbostock commented 2 months ago

Ref. esbuild’s on-resolve callback: https://esbuild.github.io/plugins/#on-resolve