Search code examples
angularassetsproduction

Angular - Assets returning 404 in library production build


I'm using ng serve to develop a Component Library in Angular.

Whenever I am running the development server, I am able to serve up assets because I have bundled them in on my project angular.json file. The project structure is like so:

angular-project
- projects
  - component-library
    - src
      - assets <- contains SVGs consumed by library
      - lib <- source code
- src
  - app
    app.component.ts
    app.component.html
    app.component.scss
    app.module.ts
angular.json <-- bundles projects/component-library/src/assets when running ng serve

I think this is where I've messed myself up. Inside my angular.json file, I have this:

 "angular-project-name": {
            "projectType": "application",
            "schematics": {
                "@schematics/angular:component": {
                    "style": "scss"
                }
            },
            "root": "",
            "sourceRoot": "src",
            "prefix": "app",
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:browser",
                    "options": {
                        "outputPath": "dist/angular-project",
                        "index": "src/index.html",
                        "main": "src/main.ts",
                        "polyfills": "src/polyfills.ts",
                        "tsConfig": "tsconfig.app.json",
                        "aot": true,
                        "assets": [
                            "src/favicon.ico",
                            "src/assets",
                            "src/manifest.webmanifest",
                            {
                                "glob": "**/*",
                                "input": "./projects/component-library/src/assets",
                                "output": "/src/assets/"
                            } <-- HERE is where I bundle everything
                        ], 
                        ...

And then I can call for assets in code like so:

export class SomeComponent {

    public get logoUrl(): string {
        switch (this.messageBarType) {
            case 'warning':
                return './src/assets/img/message-bar/string-icon-info-warn.svg';
            case 'severe-warning':
                return './src/assets/img/message-bar/string-icon-info-severe-warn.svg';
            case 'error':
                return './src/assets/img/message-bar/string-icon-info-error.svg';
            case 'blocked':
                return './src/assets/img/message-bar/string-icon-info-blocked.svg';
            case 'success':
                return './src/assets/img/message-bar/string-icon-info-success.svg';
            case 'info':
                return './src/assets/img/message-bar/string-icon-info-warn.svg';
        }

    }

}

And that works fine on the development server. But when I create a production build and push it to NPM, any call for assets returns 404. The assets folder definitely gets included with a production build as I can see it in the dist folder, and as far as I can tell, my ng-package.json file is correct:

{
    "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
    "dest": "../../dist/component-library",
    "lib": {
        "entryFile": "src/public-api.ts"
    },
    "assets": [
        "./src/lib/**/*.scss",
        "./src/assets/**"
    ]
}

I've tried moving the assets folder about, I've tried changing the paths in code to point at the right place but I have had absolutely no luck in regards to a production build. Has anyone experienced anything like this before?


Solution

  • I ended up essentially creating my own custom icons module.

    Because the assets are SVGs, and that will most likely always be the case, I created this type here:

    export interface INgxFluentDesignIcon {
        readonly name: string;
        readonly paths: Array<string>;
        readonly fill: string;
        readonly width: number;
        readonly height: number;
    }
    

    and then created a component which takes an INgxFluentDesignIcon like so:

    import { Component, Input } from '@angular/core';
    import { INgxFluentDesignIcon } from '../shared/types/ngx-fluent-design-icon.interface';
    
    @Component({
        selector: 'ngx-fluent-design-icon',
        templateUrl: './icon.component.html',
        styleUrls: ['./icon.component.html']
    })
    export class NgxFluentDesignIconComponent {
        @Input() public icon: INgxFluentDesignIcon;
    }
    
    
    <svg *ngIf="icon" [attr.width]="icon.width" [attr.height]="icon.height" [attr.viewBox]="'0 0' + ' ' + icon.width + ' ' + icon.height" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path *ngFor="let path of icon.paths" [attr.d]="path" [attr.fill]="icon.fill"></path>
    </svg>
    
    

    And the correct icon is now displayed on my components.

     public get logoUrl(): INgxFluentDesignIcon {
            switch (this.messageBarType) {
                case 'warning':
                    return NgxFluentDesignIconInfoWarn;
                case 'severe-warning':
                    return NgxFluentDesignIconInfoSevereWarn;
                case 'error':
                    return NgxFluentDesignIconInfoError;
                case 'blocked':
                    return NgxFluentDesignIconInfoBlocked;
                case 'success':
                    return NgxFluentDesignIconInfoSuccess;
                case 'info':
                    return NgxFluentDesignIconInfoWarn;
            }
        }
    

    This suits my needs perfectly. I still have no idea how to include assets with a production build of an Angular library though, so I will leave this open for a few more days.