remix-run / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.
https://remix.run
MIT License
29.79k stars 2.51k forks source link

[Bug]: font files are not moved to `_assets` directory on build #1153

Closed ties-v closed 2 years ago

ties-v commented 2 years ago

What version of Remix are you using?

1.1.1

What version of Node are you using? Minimum supported version is 14.

14.18.1

Steps to Reproduce

Example on stackblitz.com

  1. Run yarn create remix to create a new remix project.
  2. Install a fontsource package in this project, e.g: yarn add @fontsource/aguafina-script
  3. Create the main style sheet: (source)
    /* app/styles/main.css */
    body { font-family: "Aguafina Script"; }
  4. Add the font style sheet and the main style sheet to root.tsx: (source)
    
    // ...
    import fontStyleUrl from "@fontsource/aguafina-script/index.css";
    import mainStyleUrl from "~/styles/main.css";

export let links: LinksFunction = () => { return [ { rel: "stylesheet", href: fontStyleUrl }, { rel: "stylesheet", href: mainStyleUrl } ] } // ...


5. Run the development server: `yarn dev` and visit the page in you browser.

### Expected Behavior

Font files (*.woff or *.woff2) from the fontsource package are copied to the `_assets` folder. Also, the compiled version of the fontsource css points to these font files.

### Actual Behavior

Font files are not copied to the `_assets` folder. Browser receives HTTP 404 when trying to load the font.
![image](https://user-images.githubusercontent.com/1744962/146679034-e259161d-03a3-4da8-beb2-49685d9ea880.png)
ties-v commented 2 years ago

Related: https://github.com/remix-run/remix/issues/185#issuecomment-985869723

synaptiko commented 2 years ago

Using the local font from npm is one of the methods described in MUI's docs: https://mui.com/components/typography/#install-with-npm

I would expect this to work out of the box with Remix.

akbo commented 2 years ago

I found the following workaround on the Remix Discord (credit goes to @kiliman): https://discord.com/channels/770287896669978684/940920958540197889/941007998388690985

For those who don't want to / can't follow the Discord link, add this to your package.json to copy the font files to the bulid folder (change poppins to the name of the font you use):

"build": "remix build && npm run install:fonts",
"postinstall": "remix setup node && npm run install:fonts",
"install:fonts": "rsync -r node_modules/@fontsource/poppins/files/ public/build/_assets/files/"
melloware commented 2 years ago

If you are using PrimeIcons with PrimeReact then the script is...

Unix:

"build": "remix build && npm run install:fonts",
"postinstall": "remix setup node && npm run install:fonts",
"install:fonts": "rsync -r node_modules/primeicons/fonts/ public/build/_assets/fonts/",

Windows:

npm install copyfiles
"build": "remix build && npm run install:fonts",
"postinstall": "remix setup node && npm run install:fonts",
"install:fonts": "copyfiles -u 3 "./node_modules/primeicons/fonts/*" ./public/build/_assets/fonts/",
cdev-enter commented 2 years ago

Copying font files manually does not work with the latest version (1.6.4) anymore. Font file references are now cache busted.

Is there a way to disable cache busting for css or make remix copy the font files?

mcansh commented 2 years ago

looking into this a bit, looks like esbuild doesn't copy imports/urls from .css files if the loader is set to 'file'

mcansh commented 2 years ago

edit: made an example repo - https://github.com/mcansh/playground-1658515042024/tree/main

i did manage to get this working using postcss as a interim step in the build process

// ./postcss.config.js
const path = require("node:path");
const fse = require("fs-extra");

module.exports = {
  plugins: {
    "postcss-import": {},
    "postcss-url": {
      url: (asset) => {
        let { absolutePath } = asset;
        let basename = path.basename(absolutePath);
        let outDir = path.join(
          __dirname,
          "public",
          "build",
          "_assets",
          "fonts"
        );
        let destpath = path.join(outDir, basename);
        if (!fse.pathExistsSync(destpath)) {
          fse.copySync(absolutePath, destpath);
        }
        return "/" + path.join("build", "_assets", "fonts", basename);
      },
    },
  },
};
// ./styles/index.css
@import "@fontsource/aguafina-script/index.css";

using npm-run-all to run both remix and postcss in one tab

"scripts": {
  "dev:css": "postcss ./styles/index.css --output ./app/styles/index.css --watch",
  "dev:remix": "remix dev",
  "dev": "run-p dev:*"
}
// ./app/routes/root.jsx
import fontStyleUrl from './styles/index.css';
mrmartineau commented 2 years ago
"build": "remix build && npm run install:fonts",
"postinstall": "remix setup node && npm run install:fonts",
"install:fonts": "copyfiles -u 3 "./node_modules/primeicons/fonts/*" ./public/build/_assets/fonts/",

So this method works when running remix build but does not when running remix dev. For some reason when running remix dev there are two build directories generated: one in the public directory and another in the root of the repo (which looks like a duplicate of the directory in the public directory..).

My fix for this was to add a prestart script like so:

"prestart": "npm run install:fonts",
"start": "remix dev"

if you use npm run dev instead, it would look like this:

"predev": "npm run install:fonts",
"dev": "remix dev"

UPDATE: Also, I used cpy-cli instead of copyfiles so the install:fonts script would look like this for the above scenario:

"install:fonts": "cpy './node_modules/primeicons/fonts/*' public/build/_assets/fonts/"
dominikj111 commented 2 years ago

I think this should be handled by the framework and not by developers using the remix. I was wondering, so I had a look and the loader for the css is actually a "file" type also mentioned in the https://github.com/remix-run/remix/issues/185#issuecomment-1007139460. After change to "css" as esbuild should to cover it I see this in the console.

Remix App Server started at http://localhost:3001 (http://172.20.10.2:3001)
GET / 200 - - 143.124 ms
GET /[object%20Object] 404 - - 9.569 ms

Otherwise I see

...
GET /build/_assets/fonts/primeicons.ttf 404 - - 6.513 ms
GET /build/_assets/fonts/primeicons.woff 404 - - 5.325 ms

I think that esbuild has some problem here or it is used on way which doesn't allow to parse css properly.

mcansh commented 2 years ago

I think this should be handled by the framework and not by developers using the remix.

definitely, i took a quick look at this a bit ago - never opened an issue on esbuild yet, but looks like if you use a file loader on a css file it doesn't copy over the url() assets in that file, however if you use a css loader they are copied, but then css file itself is not hashed and the url doesn't get replaced in the build file

https://github.com/mcansh/esbuild-font-files

image

mcansh commented 2 years ago

looks like this is what we need to do: https://github.com/evanw/esbuild/issues/1757#issuecomment-963133064 looking into it :)

KingSora commented 2 years ago

@mcansh I've created a plugin which (hopefully) does the job.. I've tried to touch as less as possible and recycle as many build options as possible #4130

mcansh commented 2 years ago

@mcansh I've created a plugin which (hopefully) does the job.. I've tried to touch as less as possible and recycle as many build options as possible #4130

awesome! i'm gonna pull this and check it out ❤️

ayuhito commented 2 years ago

We're actually rebuilding the Fontsource website with Remix so this is huge. Thank you so much for giving up your time for this @mcansh and @KingSora!

github-actions[bot] commented 2 years ago

🤖 Hello there,

We just published version v0.0.0-nightly-a0823ed-20221015 which involves this issue. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

machour commented 2 years ago

Fixed in 1.7.3, thank you @KingSora 🎉

sergiodevelops commented 1 year ago

copyfiles

Hi Friends !!! In my case, the development server sources did not load... I share in this answer my solution (in Remix App FullStack Framework):


// in your root project:

  1. ) add "copyfiles" library in your project: yarn add copyfiles copyfiles library docs link

    // in your package.json file allocated in your root project:

  2. ) add "dev:fonts" script in your package.json For example in my case:
    ...previus lines
    "dev": "run-p dev:*",
    "dev:css": "npm run generate:css -- --watch",
    "dev:remix": "cross-env NODE_ENV=development binode --require ./mocks -- @remix-run/dev:remix dev",
    "dev:fonts": "rsync -r public/fonts/ public/build/_assets/fonts/",
    ...next lines

    // in your css file to load in your route: 3) add your @font-face css rules in your css file loaded from your route: For example in my case:

    
    @font-face {
    font-family: 'ChekoFontBold';
    src: local('ChekoFont-Bold'),
    url('fonts/Cheko Fonts/Web/ChekoFont/ChekoFont-Bold.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
    url('fonts/Cheko Fonts/Web/ChekoFont/ChekoFont-Bold.woff') format('woff'), /* Modern Browsers */
    url('fonts/Cheko Fonts/Web/ChekoFont/ChekoFont-Bold.ttf') format('truetype'), /* Safari, Android, iOS */
    url('fonts/Cheko Fonts/Web/ChekoFont/ChekoFont-Bold.svg#BloggerSans') format('svg'); /* Legacy iOS */
    font-style: normal;
    font-weight: normal;
    text-rendering: optimizeLegibility;
    }

.section-title { font-family: "ChekoFontBold" !important; text-align: center; font-size: 2rem !important; }

________________________________________
// in your css file to load in your route:
4) add your rule css className in your tsx or jsx file loaded from your route:
For example in my case: 

import React from 'react';

export default function About() { return (<div className={section}> <h1 className={section-title}>{"Good luck !"}

); }



Its worked for me !! 
Good luck !
machour commented 1 year ago

@sergioarieljuarez if 1.7.3 didn't work for you here, please open a new issue and provide a public repository reproducing the problem 🙏

universse commented 1 year ago

hi, any tip on how to get the url for the hashed font file referenced in the css file? I'm trying to preload the font via route module's links function, but esbuild throws an error when importing the font file from node_modules. image

machour commented 1 year ago

@universse please ask support questions in the Discussions tab.