Best way to import dependencies, post-Embroider?

What’s the best practice for importing dependencies for Embroider enabled apps?

One of our projects is using a @fontsource font. The package has some .css that needs to be imported, which references font files using relative paths (eg src: url(“./files/my-font.woff”)).

The @fontsource docs say to add import “@fontsource/my-font.css” to the app’s entry point and “that’s all folks”.

Today, with Embroider enabled I can add an asset/resource module to my Webpack config and all is well. :partying_face:

As I understand it, the only way to achieve this pre-Embroider would involve a series of app.import() statements in the ember-cli-build. Some to import the css, and others to import the font files and put them in a place that matched the relative paths from the css.

What I’m not clear about is which approach is “better”.

The app.import approach is less ergonomic (requires more lines of code) but I’m not sure if that’s still the preferred way of importing.

Can anyone shed a bit of light? Thanks :pray:t2:

1 Like

IIRC app.import is the “legacy” way of adding arbitrary modules to the build. Even with ember auto import a lot of that was becoming unnecessary, and Embroider can further leverage all the great build tools that have been created in the meantime (since ember-cli was created and there were no standards around modules and build tools) and all the benefits they can provide (tree shaking, code splitting, etc). So if you’re already on Embroider and it’s working I’d go with that. I’d assume ember-cli-build and all the associated APIs will be slimmed down and/or deprecated once Embroider is widely adopted.

Many thanks @dknutsen, that’s what my gut was saying too. :+1:t2:

@roomman Can you share the webpack configuration you needed to make this work? I’m facing the exact same problem. Thank you!

Sure, happy to. :grinning:

If you are using Webpack 5, you can take advantage of Asset Modules and add something like this to your config:

module: {
  rules: [
    {
      test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
      type: 'asset/resource',
      generator: {
        filename: 'fonts/[hash][ext][query]',
      },
    },
  ],
},

In this case, filename determines where things land in your dist folder.

If your project is < Webpack 5 then you will need to add file-loader as a dev dependency, and this to your config instead:

module: {
  rules: [
    {
      test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'fonts/',
          },
        },
      ],
    },
  ],
}

This time, outputPath determines where files land in your dist folder.

Let me know if it works for you :+1:t2:

@roomman Thanks a lot!

I gave this a try but no matter what I tried the fonts don’t appear in the dist folder. We use webpack@5 in the project and I tried this config snippet:

module: {
  rules: [
    {
      test: /\.(woff|woff2)$/i,
      type: 'asset/resource',
      generator: {
        filename: 'fonts/[name].[hash][ext][query]',
      },
    },
  ],
},

Just as in your example, we have the @fontsource CSS file refer to the font files using a relative path in url(...). That’s enough to try to load the font but my suspicion is that Webpack doesn’t “discover” the font files so it doesn’t add them to the build.

I also added one of the font files inside app/assets/fonts and have the following snippet in app/styles/app.css to get it working:

@font-face {
  font-family: 'InterVariable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('../assets/fonts/inter-latin-variable-wghtOnly-normal.woff2') format('woff2');
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

body {
  font-family: 'InterVariable';
}

This is almost exactly what the Webpack guides describe in their “Loading fonts” example but it still doesn’t work - the font file doesn’t appear in dist/assets/fonts.

Do you have an insight into why that could be? How does your code refer to the font? I’m trying to understand how Webpack decides to bundle a resource (in this case, a font) into its output.

Thank you.

I’m just finding my way around Webpack so I don’t have any real insights to share, sorry.

What I can share is this repo, which illustrates the setup I’ve used in my project. There are two commits, one to enable Embroider, and then a second to setup the font. This is enough to get the files into my dist folder, for Webpack to bundle them into the primary chunk, and for the font to render when you view the Welcome Page.

I suspect adding import '@fontsource/inter/variable.css'; to the top of app.js and removing the @font-face declaration from app.css will help. That way, Webpack will be guided to the original font files by the declarations in the @fontsource css file?

I hope this helps you! :+1:t2:

It did, thank you very much!

The problem I had is that I tried to pull in the font CSS file that defined the font-faces using @import in a CSS file whereas you used an ES6 import in app.js. Not sure why that has fixed it but it did, thank you! :pray:t3:

I would have expected either to work.