Katya
Katya

Reputation: 1

Lazy import component from the library's secondary entry point with absolute path using library's component

UPDATES: Link to Repo

Read a lot of documentation and checking examples, but unfortunately I have the worse combination ever. Lazy loading, dynamic import, absolute path with variable :

I have Angular 15 app with 2 projects, library and application which should refer to library's components using absolute path (because I need library to be used same way from another Angular app). Library has standalone components - secondary entry points. One of the components is tricky. It is creating components in ViewContainerRef container from REST API response. I simplified it in repo, so it works with provided example data.

When I try a relative path it works well

const module = await import(`../../${item.path}/src/${item.path}.component`);

But I need an absolute path here to use from other projects.

I tried thousands ways to resolve it, edited tsconfig to include items, edited package to export items. Always have an errors. Most common is:

./projects/mycompany/sdk/dynamic-container/src/dynamic-container.component.ts:21:31-95 - Error: Module not found: Error: Can't resolve '@mycompany/sdk' in 'C:\Users\yekat\apps\new\our-library\projects\mycompany\sdk\dynamic-container\src'

Please help if you have any ideas how to resolve it. I spent days and nights and have no answer...

I cannot import all components without lazy loading because there could be a lot of them or a few and I don't need to load the whole library.

The following long message it what I started with. Now I have a repo and you can reproduce.

Thank you in advance!!!

OLD QUESTION I have 2 Angular 15 apps which I try to make work together. First one is the library with standalone components - secondary entry points. One of the components is tricky. It is creating components in ViewContainerRef container from API response.

component.module = await import(`../../../${itemPath}/src/${componentName}.component`);
...
this.componentRef = this.containerRef?.createComponent(component.module[component.moduleName]);

In this app I have a sandbox, its module importing my tricky dynamic component relatively.

import { DynamicContainerComponent } from '../../../mycompany/sdk/base-components/dynamic-container/src/dynamic-container.component';

Then it is displaying it in html. It works really good within Library app. Lazy loading works as expected, building process creating folder dist with

esm2020/...
fesm2015/...
fesm2020/...
....

Serving process looks good, shows the chunks been created:

Initial Chunk Files                                                                                     | Names         |   Raw Size
vendor.js    
                                                                                           .....
| Initial Total |    2.69 MB
.....
Lazy Chunk Files                                                                                        | Names         |   Raw Size
....
projects_mycompany_sdk_base-components_drop-down-list_src_drop-down-list_component_ts.js              | -             |  867.52 kB |
....
projects_mycompany_sdk_base-components_plain-html_src_plain-html_component_ts.js                      | -             |    2.26 kB |

It doesn't have my dynamic component because it's been imported by sandbox and included in Initial Chunk Files.

tsconfig.json 
{
  "compileOnSave": false,
  "compilerOptions": {
    "resolveJsonModule": true,
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "strictPropertyInitialization": false,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "typeRoots": [
      "node_modules/@types"
    ],
    "paths": {
      "@mycompany/sdk/*": [
        "projects/mycompany/sdk/*"
      ],
      "@mycompany/sdk": [
        "projects/mycompany/sdk"
      ]
    },
    "target": "ES2022",
    "module": "ES2022",
    "useDefineForClassFields": false,
    "lib": [
      "ES2022",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}

angular.json:

    "projects": {
        "@mycompany/sdk": {
            "projectType": "library",
            "root": "projects/mycompany/sdk",
            "sourceRoot": "projects/mycompany/sdk",
            "prefix": "lib",
            "schematics": {
                "@schematics/angular:component": {
                "standalone": true,
                "style": "scss"
                },
                "@schematics/angular:directive": {
                "standalone": true
                },
                "@schematics/angular:pipe": {
                "standalone": true
                }
            },
            "architect": {
                "build": {
                "builder": "@angular-devkit/build-angular:ng-packagr",
                "options": {
                    "project": "projects/mycompany/sdk/ng-package.json"
                },
                "configurations": {
                    "production": {
                    "tsConfig": "projects/mycompany/sdk/tsconfig.lib.prod.json"
                    },
                    "development": {
                    "tsConfig": "projects/mycompany/sdk/tsconfig.lib.json"
                    }
                },
                "defaultConfiguration": "production"
                },
                "test": {
                "builder": "@angular-devkit/build-angular:karma",
                "options": {
                    "tsConfig": "projects/mycompany/sdk/tsconfig.spec.json",
                    "polyfills": ["zone.js", "zone.js/testing"]
                }
                }
            }
        },
...

I use Verdaccio to publish this library and it works well. When I import simple components from this library into another app, they work well. But when it comes to my dynamic-container, I gen an error. But let me tell first how Application 2 works. app component is similar to what sandbox in App 1 has, but importing from package :

import { DynamicContainerComponent } from '@mycompany/sdk/base-components/dynamic-container';

(When I import other components, they work. But they don't have this lazy import from relative paths.) So DynamicContainerComponent takes a value which is Observable response from API call. DynamicContainerComponent itself decides what component it should load.

I tried to use different path in tsconfig:

"paths": {
  "../../../base-components/*" : [
     "node_modules/@mycompany/sdk/base-components/*"
  ],
  "../../../base-components" : [
    "node_modules/@mycompany/sdk/base-components"
  ],
  "base-components": [
    "node_modules/@mycompany/sdk/base-components"
  ],
  "@mycompany/sdk/*": [
    "node_modules/@mycompany/sdk/*"
  ],
  "./base-components": [
    "node_modules/@mycompany/sdk/base-components"
  ]
}

angular.json:

...
  "projects": {
    "test-lib": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/test-lib",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": [
              "zone.js"
            ],
            "tsConfig": "tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                .......
              ],
              "outputHashing": "all"
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            }
          },
          "defaultConfiguration": "production"
        },
....

when building this app, dist folder gets lot of weirdly called files: dist

(lot of these files and they do contain minimized library components.) when serve i can see it is also creating chunks:

Initial Chunk Files                                                                                     | Names         |   Raw Size
vendor.js                                                                                               | vendor        |    2.40 MB |
....

| Initial Total |    2.96 MB

Lazy Chunk Files                                                                                        | Names         |   Raw Size
...
        
node_modules_mycompany_sdk_esm2020_base-components_plain-html_src_plain-html_component_mjs.js         | -             |    2.83 kB |
...

When running in browser i see error: error

I assume I am missing something in App 2 configuration.

If you suggest to replace relative path

../../../${itemPath}/src/${componentName}.component

with @mycompany/sdk/${itemPath}/src/${componentName}.component) I tried, and in this case I gen an error on serving library

./projects/mycompany/sdk/base-components/dynamic-container/src/dynamic-container.component.ts:39:31-105 - Error: Module not found: Error: Can't resolve '@mycompany/sdk' in 'C:\Users\yekat\mw10-front-end-components\projects\mycompany\sdk\base-components\dynamic-container\src'

and in App2

./node_modules/@mycompany/sdk/fesm2020/mycompany-sdk-base-components-dynamic-container.mjs:38:31-105 - Error: Module not found: Error: Can't resolve '@mycompany/sdk' in 'C:\Users\yekat\apps\new\test-lib\node_modules\@mycompany\sdk\fesm2020'

Upvotes: 0

Views: 751

Answers (1)

Katya
Katya

Reputation: 1

Solved it by specifying all imports in separate service. In this case webpack is happy and lazy loading still works. I wish I could use dynamic path and lazy loading together without specifying imports for all objects. But it seems to be impossible

Upvotes: 0

Related Questions