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.