angularangular-materialangular-libraryng-packagr

Material doesn't work in Angular 13 library project


I have an Angular 13 library on the go. It works with the other modules I have already got in there, but this new module I'm moving into place uses material, and it's not happy.

The new module itself is fine and builds until I make any kind of use of material.

In theory I have it all set up correctly; I have it added to the workspace dependencies, and to the project peer dependencies.

Workspace root:

enter image description here

Project:

enter image description here

I'm importing the relevant things into the module itself (imports: [MatCheckboxModule] etc.) - I've been using material for a good few years now so know the drill, but this is the first library I'm slapping together.

I have literally removed every component from the module other than this one little checkbox component to limit the scope of it all and try to get it working.

Remove the mat-checkbox element? Builds.

Put mat-checkbox back in? Errors.

The only difference between the two is whether there's a <mat-checkbox> element in the template.

The errors are the standard expected when modules don't build... xyz is not a known element, Can't bind to xyz since it isn't a known property of mat-checkbox - all the standard things when you've got something broken in your module.

ng add tells me that everything is good:

enter image description here

... And yet...

So, what have I missed? What can I try? Why is this so painful? When will it all end?

(Oh god, once I get past this one the next module will have AG-Grid in it; I hope it's not as painful as getting material in...).

P.S. I have seen this question and others, but the simple 'just run ng add' answers clearly aren't cutting it...


Here's some additional details. The package.json files I've already shown, so I'll skip those...

Project structure:

root
  - projects
    - core
      - src
        - lib
          - shared
            - modules
              angular-material.module
              ...
            shared.module
            ...
          - ui-elements
            - components
              - checkbox
                checkbox.component
                ...
              - ...
            ui-elements.module
            ...
          - icons
            ...
          - etc...

Each module under lib has its own index.ts (consists purely of export * from './public-api';), its own ng-package.json (similarly identical contents across them), and public-api.ts which is the only file where the contents differ.

ng-package.json content for completeness:

{
  "$schema": "../../../../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../../../../dist/core",
  "lib": {
    "entryFile": "public-api.ts"
  }
}

My root tsconfig.json file paths so that the modules can reference each other appropriately with import { x } from '@my-namespace/core/src/lib/y';:

"paths": {
  "@my-namespace/core/*": [
    "projects/core/*",
    "projects/core"
  ],
  "@my-namespace/core": [
    "dist/core/*",
    "dist/core"
  ]
}

My angular-material.module (abbreviated):

import { MatCheckboxModule } from '@angular/material/checkbox';

const modules = [
    MatCheckboxModule,
    etc...
];

@NgModule({
  exports: [...modules]
})

My ui-elements.module (abbreviated):

import { AngularMaterialModule } from '@my-namespace/core/src/lib/shared';
import { CheckboxComponent } from './components/checkbox/checkbox.component';

const modules = [
    AngularMaterialModule,
    etc...
];

const components = [
    CheckboxComponent
];

@NgModule({
    imports: [...modules],
    declarations: [...components],
    exports: [...modules, ...components]
})

Shared public-api.ts:

export * from './modules/angular-material.module';

and ui-elements public-api.ts:

export * from './components/checkbox/checkbox.component';

... all the way down to my angular.json:

"projects": {
  "@my-namespace/core": {
    "projectType": "library",
    "root": "projects/core",
    "sourceRoot": "projects/core/src",
    "prefix": "tca",
    "architect": {
      "build": {
        "builder": "@angular-devkit/build-angular:ng-packagr",
        "options": {
          "project": "projects/core/ng-package.json"
        },
        "configurations": {
          "production": {
            "tsConfig": "projects/core/tsconfig.lib.prod.json"
          },
          "development": {
            "tsConfig": "projects/core/tsconfig.lib.json"
          }
        },
        "defaultConfiguration": "production"
      },
      "test": {
        "builder": "@angular-devkit/build-angular:karma",
        "options": {
          "main": "projects/core/src/test.ts",
          "tsConfig": "projects/core/tsconfig.spec.json",
          "karmaConfig": "projects/core/karma.conf.js"
        }
      }
    }
  }
},

Ok, I think I'm out of files to worry about. So, anyone with the patience to read and understand all of that and tell me what I'm missing and where so that this stupid thing will accept the existence of material into its cold, dead heart?

Oh, one more, actually, the actual core project public-api.ts content itself:

export * from '@my-namespace/core/src/lib/shared';
export * from '@my-namespace/core/src/lib/ui-elements';
export * from '@my-namespace/core/src/lib/etc...';

Even more details!

Attempted a minimal reproduction with a fresh project, which manages to get one step further than mine.

Same setup, the elements module builds if material is imported directly into it (which mine doesn't). If mine did, I'd cut my losses and have each do their own, so maybe I cut back and try and get that step working at least...

When switching it to import the shared module instead, build fails with a small spam of these rootDir errors:

1 export * from './shared.module';
 
projects/core/src/lib/shared/public-api.ts:1:15 - error TS6059: File 'C:/.../libtest/lib-mat-test/projects/core/src/lib/shared/shared.module.ts' is not under 'rootDir' 'C:\...\libtest\lib-mat-test\projects\core\src\lib\elements'. 'rootDir' is expected to contain all source files.

and

projects/core/src/public-api.ts:5:15 - error TS6307: File 'C:/.../libtest/lib-mat-test/projects/core/src/lib/elements/index.ts' is not listed within the file list of project 'C:/.../libtest/lib-mat-test/projects/core/tsconfig.lib.prod.json'. Projects must list all files or use an 'include' pattern.

5 export * from '@my-namespace/core/src/lib/elements';

and

1 export * from './core.component';
 
projects/core/src/lib/elements/public-api.ts:1:15 - error TS6307: File 'C:/.../libtest/lib-mat-test/projects/core/src/lib/elements/core.component.ts' is not listed within the file list of project 'C:/.../libtest/lib-mat-test/projects/core/tsconfig.lib.prod.json'. Projects must list all files or use an 'include' pattern.
  The file is in the program because:
    Imported via './core.component' from file 'C:/.../libtest/lib-mat-test/projects/core/src/lib/elements/public-api.ts'
    Imported via './core.component' from file 'C:/.../libtest/lib-mat-test/projects/core/src/lib/elements/core.module.ts'

I've had less than 3 hours sleep, so brain can't process these right now...


Solution

  • Ok, I got it successfully building. Testing now...

    I've had to scale back my usage of shared modules a fair amount. I no longer have angular-material.module in shared - that's the first breakage, as shown by my minimal repro. E.g. having a shared module and doing imports as import {AngularMaterialModule} from '@my-namespace/core/src/lib/shared'.

    Also failed was to knock the shared module down to just a directory of files, but it still didn't like that... import {AngularMaterialModule} from '[@ or .., neither worked]';

    So each of the sections will have their own material imports (or grouped material module) rather than having a single one to share.

    At least it seems like I can move on with my life, even though it's not as nice as I'd hoped it would be.