Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trouble loading fonts from Semantic UI #826

Closed
sholladay opened this issue Feb 17, 2021 · 10 comments
Closed

Trouble loading fonts from Semantic UI #826

sholladay opened this issue Feb 17, 2021 · 10 comments

Comments

@sholladay
Copy link

I use Semantic UI React as my component library. It's generally combined with semantic-ui-css to provide CSS styles. Unfortunately, something about the @font-face rules in semantic-ui-css trips up esbuild.

With an index.jsx entry point file of simply:

import 'semantic-ui-css/semantic.css';

... I get errors when I run esbuild, such as:

❯ npx esbuild index.jsx --bundle --sourcemap --outdir=build
 > node_modules/semantic-ui-css/semantic.css: error: Could not resolve "./themes/default/assets/fonts/icons.eot?#iefix"
    7672 │   src: url("./themes/default/assets/fonts/icons.eot?#iefix") format('embedded-opentype'), url("./themes/default/assets/...
         ╵            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 > node_modules/semantic-ui-css/semantic.css: error: Could not resolve "./themes/default/assets/fonts/icons.svg#icons"
    7672 │ ...efault/assets/fonts/icons.ttf") format('truetype'), url("./themes/default/assets/fonts/icons.svg#icons") format('svg');~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 > node_modules/semantic-ui-css/semantic.css: error: Could not resolve "./themes/default/assets/fonts/outline-icons.eot?#iefix"
    12951 │   src: url("./themes/default/assets/fonts/outline-icons.eot?#iefix") format('embedded-opentype'), url("./themes/defaul...
          ╵            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I'm not sure what the best approach is for dealing with these assets, but the paths are correct and the files do exist on disk. It works with my current Rollup setup. Do I need to specify some kind of loader? It would be nice if esbuild could give a more actinable error message.

esbuild v0.8.46
Node v15.7.0
semantic-ui-css v2.4.1

@evanw
Copy link
Owner

evanw commented Feb 17, 2021

This happens because the file does not have the .eot?#iefix extension. This has come up once before too: #743. I suppose it's worth adding the workaround into esbuild itself since it looks like both Parcel and Webpack special-case these kinds of paths.

@evanw evanw closed this as completed in 91cf837 Feb 17, 2021
@sholladay
Copy link
Author

@evanw Many thanks for the fast response and fix. I've confirmed that the fix works. I also love the way you solved it. I haven't quite gotten my build working yet, but feeling really good about esbuild. :)

Is there a succinct way to configure a loader for multiple file types? If I understand correctly, I think I need to use the dataurl loader on all of these images, fonts, and SVGs that are being loaded by url() in Semantic's CSS file. That's making my bundle command pretty unwieldy.

npx esbuild lib/index.jsx --bundle --sourcemap \
  --outdir=build \
  --define:process.env.NODE_ENV=\"production\" \
  --loader:.png=dataurl \
  --loader:.woff=dataurl \
  --loader:.woff2=dataurl \
  --loader:.eot=dataurl \
  --loader:.ttf=dataurl \
  --loader:.svg=dataurl

@evanw
Copy link
Owner

evanw commented Feb 18, 2021

Is there a succinct way to configure a loader for multiple file types?

No, there isn't really. Each file type is explicit. You could write a plugin I suppose but the plugin code would be bigger than just using the loader commands.

That's making my bundle command pretty unwieldy.

The CLI is only really intended for simple uses of esbuild. More complex uses should probably use the API instead:

require('esbuild').build({
  entryPoints: ['lib/index.jsx'],
  bundle: true,
  sourcemap: true,
  outdir: 'build',
  define: {
    'process.env.NODE_ENV': '"production"',
  },
  loader: {
    '.png': 'dataurl',
    '.woff': 'dataurl',
    '.woff2': 'dataurl',
    '.eot': 'dataurl',
    '.ttf': 'dataurl',
    '.svg': 'dataurl',
  },
})

This is about equivalent verbosity of course, but having it in a separate file and being able to use real programming language syntax makes it less unwieldy IMO. You could also then use code to construct the loader object from an array of file extensions, for example.

@sholladay
Copy link
Author

For me, the main selling point of esbuild is the simple DX and good defaults (as opposed to performance). I haven't needed any plugins yet and for the most part everything "just works". The CLI is an important part of the simple DX because it makes it easy to switch between modes (e.g. turn watch or minify mode on/off) during development.

I wonder if you would be willing to support a config file so that we could put some options in a file but still use the CLI?

Rollup supports this via the --config flag, which enables loading a config file (rollup.config.js by default).

For example, my Rollup config file looks like this:

import babel from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import json from '@rollup/plugin-json';

// `npm run build` -> `isDev` is false
// `npm run dev` -> `isDev` is true
const isDev = Boolean(process.env.ROLLUP_WATCH);

export default {
    input  : 'lib/index.jsx',
    output : {
        file      : 'build/app.js',
        format    : 'esm',
        sourcemap : true
    },
    plugins : [
        resolve({
            extensions : ['.js', '.json', '.jsx'],
            browser    : true
        }),
        commonjs({ include : 'node_modules/**' }),
        postcss({
            modules : {
                globalModulePaths : [
                    /node_modules\/react-credit-cards\/es\/styles-compiled.css/u,
                    /node_modules\/react-image-crop\/dist\/ReactCrop.css/u,
                    /node_modules\/semantic-ui-css\/components\/.*\.css/u
                ]
            }
        }),
        // This is needed to fix React assuming the browser has `process`
        replace({ 'process.env.NODE_ENV' : JSON.stringify(isDev ? 'development' : 'production') }),
        json({ preferConst : true }),
        babel({
            babelHelpers : 'bundled',
            exclude      : 'node_modules/**'
        }),
        !isDev && terser()
    ]
};

The beauty of this is that it is dynamic like the API but it preserves the convenience of the CLI. It also simplifies npm scripts, mine are just:

"scripts": {
    "build": "rollup --config",
    "dev": "rollup --config --watch"
},

If it weren't for the lack of a default loader for some of those file types, I wouldn't even feel the need for a config file, I'd probably just keep everything in npm scripts, which would be even more awesome.

@evanw
Copy link
Owner

evanw commented Feb 21, 2021

I wonder if you would be willing to support a config file so that we could put some options in a file but still use the CLI?

No, I am not willing to do this. I think the API is a better design than a config file. Using the API is about the same amount of code as using a config file but it's much more flexible because it doesn't take away your abstraction capabilities. I went into this in more detail in #536 if you're interested.

The CLI is an important part of the simple DX because it makes it easy to switch between modes (e.g. turn watch or minify mode on/off) during development.

The nice thing about the API vs. a config file is you are in control, not esbuild. Doing something like that is trivial with minify: process.argv.includes('--minify') if that's what you want.

@sholladay
Copy link
Author

Alright, well thank you for considering. I'll make a wrapper CLI and publish it to npm in case others find it useful. FWIW, I don't see these things as mutually exclusive. An API is great for complex use cases, but it's never going to compete with a CLI in terms of ergonomics, which I'm sure is why esbuild has a CLI to begin with.

@izatop
Copy link

izatop commented Nov 22, 2021

@evanw what's about the package resolution syntax in css like this ~@fortawesome/fontawesome-free/...? This method uses in font loaders and IDE (such as WebStorm, VSCode, webpack and so on). I use this too in my build system on top of esbuld.
I want to preserve hashes and queries in the css url function when my plugin resolves package resources in node_modules. What can i do to help fix this?

And thank you so much for esbuild and great work!

@AStoker
Copy link

AStoker commented May 8, 2023

@izatop , you ever figure that one out? Just started an Angular 16 upgrade with experimental ESBuild support, and it's failing on font files from fortawesome.

@izatop
Copy link

izatop commented May 9, 2023

@AStoker I solve that case by plugin which resolves paths starting at ~@ in sass files.

@AStoker
Copy link

AStoker commented May 9, 2023

Hmmm, yeah, I don't think that's the case that's going on with me (the tilde is actually deprecated with latest sass). I'll keep digging, but thanks for the reply!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants