2

The use case is simply to be able to use any FontAwesome icon, from a string, without necessarily knowing what those icons are going to be until runtime, without the client having to download icons they won't be using.

I'm using angular-fontawesome, using the Icon Library approach described here: https://github.com/FortAwesome/angular-fontawesome/blob/master/docs/usage/icon-library.md

With this example syntax:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faCoffee } from '@fortawesome/free-solid-svg-icons';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, FontAwesomeModule],
  bootstrap: [AppComponent]
})
export class AppModule {
  constructor(library: FaIconLibrary) {
    // Add an icon to the library for convenient access in other components
    library.addIcons(faCoffee);
  }
}

So, ideally it would be possible to make the faCoffee part dynamic. I know that static analysis can see that faCoffee is being imported, so it knows to bundle it, and that if it were a dynamic variable it can't possibly guess what export would be required - but then I'm just at that point wondering if there is anything in Webpack, or any other utility, which would allow the code to be included in the build but only downloaded to the client if necessary at runtime.

I've tried a few things out.

I was aware of this attempt, but this is Vue, and it may not even work: https://github.com/FortAwesome/vue-fontawesome/issues/170#issuecomment-484544272

I know you can abandon tree-shaking and use deep imports, as described here: https://fontawesome.com/v5/docs/apis/javascript/tree-shaking#alternative-to-tree-shaking-deep-imports

i.e.

import { faCoffee } from '@fortawesome/free-solid-svg-icons/faCoffee'

so I've tried an approach around that, based on https://dmitripavlutin.com/ecmascript-modules-dynamic-import/#1-dynamic-import-of-modules.

const iconModule = from(import('@fortawesome/pro-duotone-svg-icons/faCoffee'));

return iconModule.pipe(map(module => module[fullIconName] as IconDefinition));

This obviously works, you can then just add the IconDefinition to the library - and within a switch statement it means that the faCoffee bundle is only loaded when the code is executed at runtime.

However if you try and do something dynamic:

const getPath = (fullIconName: string) => `@fortawesome/pro-duotone-svg-icons/${fullIconName}`;

const iconModule = from(import(getPath(fullIconName)));

return iconModule.pipe(map(module => module[fullIconName] as IconDefinition));

Then quite understandably it doesn't work as it hasn't been bundled.

Error: Cannot find module '@fortawesome/pro-duotone-svg-icons/faCoffee'

And indeed the import documentation also states that the best that might be achievable is to import the entire file, see: https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import https://javascript.info/modules-dynamic-imports#the-import-expression

This answer also suggests it might not be possible: https://stackoverflow.com/a/55744858/1061602

However, this one suggests it might be possible with the right approach: https://stackoverflow.com/a/65298694/1061602

I can see there is a bunch of information here on dynamic imports and code splitting, and I've tried a few things out, but I can't quite figure out what it is I should be doing. https://webpack.js.org/guides/code-splitting/#dynamic-imports

There might be some other plugin for FontAwesome I could use instead.

Alternatively, someone may have written a massive switch statement along the lines of

switch (iconName) {
    case 'faCoffee':
       /* import faCoffee */
    case 'faSomethingElse':
       /* import faSomethingElse */

which would do the trick too.

Adam Marshall
  • 3,010
  • 9
  • 42
  • 80
  • Don't you just need to inline the import path to make it statically analyzable? So that Webpack can build the context and bundle all necessary files, i.e. ``import(`@fortawesome/pro-duotone-svg-icons/${fullIconName}`)`` instead of the `getPath ` function call? – Yaroslav Admin Mar 03 '22 at 19:31
  • you'd have hoped so, but you just get a `[webpack-dev-server]` error `Module not found: Error: Can't resolve '@fortawesome/pro-duotone-svg-icons'`. It is like it ignores the bit at the end. – Adam Marshall Mar 04 '22 at 08:25
  • it is the same if you do `import(\`@fortawesome/pro-duotone-svg-icons/${fullIconName}.js\`)` too - I'd see that around – Adam Marshall Mar 04 '22 at 08:26
  • 2
    Seems to work just fine for me. Here is the [repo](https://github.com/devoto13/fa-dynamic-icon-load). Clone, `npm install && npm start`, enter `faUser` into the field and click "Fetch" button. I don't use the icon library approach, but it should be trivial to change the code to use it if necessary. – Yaroslav Admin Mar 04 '22 at 12:19
  • 1
    this does seem to work, thank you for sharing – Adam Marshall Mar 04 '22 at 15:20
  • from investigation, I managed to break yours by setting it to the same font-awesome version I was using (v6 instead of v5) - so there might be something in there. – Adam Marshall Mar 04 '22 at 17:26
  • @YaroslavAdmin Please make this an answer as it's really helpful. Works a charm! – PatrickS Oct 29 '22 at 05:37

0 Answers0