Angular is unable to resolve module path of a custom NPM package

848 views Asked by At

I'm developing an Angular 13 custom library using NPM V8.19.3, and it makes use of Dynamsoft WebTwain v18.1.0 library to manage a scanner. It should be later consumed locally - e.g. via NPM link - by an Angular application in the same workspace for development reasons, later it will published on an NPM registry and made available for "real" applications. I started a new workspace because it is simply a "proof of concept", if it works will be made the same job on the real one.

Below I will summarize the context and the steps I followed.

  • create an empty workspace
  • add to it two projects, one of type library (poc-scan-package) and one of type application (poc-scan-showcase)
  • add to the application its own package.json (so that the dependencies won't fall in the common one)
  • add the Dynamsoft WebTwain library as bundled dependency of my custom package (following this link as starting guide, but adapted to be used in a package instead of into an application) and
  • add to the common package.json a script to build the package and copy the required assets to the built package as declared in the starting guide
  • create a global link to my "dist/<package name>" folder using npm link and use that link in the application project
  • add to angular.json "projects/<application name>/architect/build/options/assets" the path where I copied the WebTwain assets
  • last step has been to change the html of the application to refer the package component, and the package component itself.

After these steps these are the relevant files/sections:

Workspace content

angular.json
{
    ...
    "projects": {
        ...
        "poc-scan-showcase": {
            "projectType": "application",
            "root": "projects/poc-scan-showcase",
            ...
            "architect": {
                "build": {
                    "options": {
                        ...
                        "assets": [
                            ...,
                            {
                                "glob": "**/*",
                                "input": "projects/poc-scan-showcase/node_modules/poc-scan-package/assets/dwt-resources/dist/",
                                "output": "assets/dwt-resources"
                            }
                        ]
                    }
                }
            }
        }
    }
}
package.json - build command:
"scripts": {
    "build:lib": "npm run build -- --project poc-scan-package && mkdir -p ./dist/poc-scan-package/assets/dwt-resources && cp ./projects/poc-scan-package/node_modules/dwt/dist ./dist/poc-scan-package/assets/dwt-resources -r"
}

Library content

package.json
{
    "name": "poc-scan-package",
    "version": "0.0.1",
    "peerDependencies": {
        "@angular/common": "^13.3.0",
        "@angular/core": "^13.3.0"
    },
    "dependencies": {
        "dwt": "^18.1.0",
        "tslib": "^2.3.0"
    },
    "bundledDependencies": [
        "dwt"
    ]
}
ng-package.json
{
    "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
    "dest": "../../dist/poc-scan-package",
    "lib": {
        "entryFile": "src/public-api.ts"
    },
    "allowedNonPeerDependencies": ["dwt"]
}
poc-scan-package.component.ts
import { Component, OnInit } from "@angular/core";
import Dynamsoft from "dwt";
import { WebTwain }  from "dwt/dist/types/WebTwain";
@Component({
    selector: "lib-poc-scan-package",
    template: `
        <button (click)="acquireImage()">Acquire Images</button>
        <div id="dwtcontrolContainer"></div>
    `
})
export class PocScanPackageComponent implements OnInit {
    DWObject: WebTwain | any = null;
    ngOnInit(): void {
        .
        .
        .
        Dynamsoft.DWT.RegisterEvent("OnWebTwainReady", () => {
            this.Dynamsoft_OnReady();
        });
        Dynamsoft.DWT.ProductKey = "YOUR-PRODUCT-KEY";
        Dynamsoft.DWT.ResourcesPath = "assets/dwt-resources"; // <--expected path for setup delivery
        Dynamsoft.DWT.Load();
    }
    .
    .
    .
}

Application content

app.component.ts
@Component({
selector: "poc-root",
    template: ` <lib-poc-scan-package></lib-poc-scan-package> `,
    styles: [],
})
export class AppComponent {
    title = "poc-scan-showcase";
}

So both the Dynamsoft object and the WebTwain interface seems to be not found during runtime. This is part of the output of the application build:

./dist/poc-scan-package/fesm2015/poc-scan-package.mjs:3:0-28 - Error: Module not found: Error: Can't resolve 'dwt' in '`/home/<username>/repos/test/poc/EmbedScanIntoPackage/dist/poc-scan-package/fesm2015`'

Error: dist/poc-scan-package/lib/poc-scan-package.component.d.ts:2:26 - error TS2307: Cannot find module 'dwt/dist/types/WebTwain' or its corresponding type declarations.

`import { WebTwain } from "dwt/dist/types/WebTwain";`
                           ~~~~~~~~~~~~~~~~~~~~~~~~~

I think to understand that the compiler is right because the built library has the expected directory remapped to "assets/dwt-resources/dist/types", but I'm not sure why it has to look there.

I'm thinking to unlink the "dist" directory and make NPM point to the source library project, but every example I've ever seen make it point to it.

Here the link to the repo with the work I did up to now.

Let me know if I'm missing some useful information.

1

There are 1 answers

5
yushulx On

You can refer to my ngx-web-document-scanner project, which is a custom Angular module built with Dynamic Web TWAIN.

The projects/ngx-web-document-scanner/package.json file is very simple. You just add dwt as the peer dependency:

"peerDependencies": {
    "@angular/common": "^14.1.0",
    "@angular/core": "^14.1.0",
    "dwt": "^17.3.1"
  },

When creating an Angular project with the custom Angular module, add the asset output directory of Dynamic Web TWAIN resources in the angular.json file:

"assets": [
    "src/favicon.ico",
    "src/assets",
    {
      "glob": "**/*",
      "input": "./node_modules/dwt/dist",
      "output": "assets/dynamic-web-twain"
    }
],

In the meantime, you need to specify the resource path in TypeScript code:

import { Injectable, Optional } from '@angular/core';
import Dynamsoft from 'dwt';

export class DocumentScannerServiceConfig {
  licenseKey = '';
  resourcePath = '';
}

@Injectable({
  providedIn: 'root'
})

export class NgxDocumentScannerService {

  constructor(@Optional() config?: DocumentScannerServiceConfig) { 
    if (config) { 
      Dynamsoft.DWT.ProductKey = config.licenseKey;
      Dynamsoft.DWT.ResourcesPath = config.resourcePath;
    }
  }
}

Try https://www.npmjs.com/package/ngx-web-document-scanner

npm i ngx-web-document-scanner