8

Does an NPM package need to be modified to be compatible with Angular 2 (eg. add in typings, make directives for them) or will any existing package work? If they're not all compatible, how do I know what is and what is not compatible?

For example, say I want to import this package (https://github.com/pvorb/node-md5). I'm aware there is a ts-md5 package for angular 2 to do md5 - I'm just using this package as an example.

How would I get this to work?

I've installed it using

npm install md5 --save
npm install @types/md5 --save

but I can't seem to be import it

import {md5} from 'md5';

I get this error message after I try to run

Module '"/Users/xxx/Source/tempProjects/ngUnderscore/node_modules/@types/md5/index"' resolves to a non-module entity and cannot be imported using this construct.

I'm not sure what this message means. Does it mean that in its current state, the package is not compatible or am I trying to use the package incorrectly?

Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
Diskdrive
  • 18,107
  • 27
  • 101
  • 167

5 Answers5

7

I managed to make it work using declare and require instead of import (import won't work for this non-module lib)

declare const require: any;
const md5 = require('md5');

If you don't want to workaround import like this, you can try using Typescript MD5 implementation called ts-md5. Then import like the one below should work. (referenced from this question)

import { Md5 } from 'ts-md5/dist/md5'

If there is no TS implementation, you can search for the types in DefinitelyTyped and then simply install package by npm i --save-dev @types/{package-name}

Vojtech
  • 2,756
  • 2
  • 19
  • 29
  • Thanks for getting it working. I guess I'm looking for more an answer in general. There's tons of NPM libraries - some of them aren't actively maintained, some of them don't have TS declaration files, some of them aren't modular - if I want to use it in an angular project, what are the standard ways for me to get it working in Angular 2? Can I always use the declare const require: any method? – Diskdrive Apr 03 '17 at 01:52
  • 1
    You can use the declaring and requiring workaround for any JS lib/file. There are also ways to use 3rd party javascript without requiring it. You can for example link it in index.html or use the webpack (or any other build tool) to include it in a bundle. Then you will need only to declare the used lib/module or create a custom typings to stop a compiler from throwing errors. – Vojtech Apr 03 '17 at 18:51
2

If the library works on your project depends on many factors: your Angular version, your TypeScript version, etc.

This said, is obvious that we should check the library's documentation and see which dependencies has and its versions, and of course the library should be the Angular 2 version of itself. Following your example, there are several md5 libraries but if you use TypeScript you should maybe consider this one: https://www.npmjs.com/package/ts-md5

If we have all that covered but still there is something not working because of some type of incompatibility, like for example:

My version of angular is 2, the library I just installed works with Angular 4. I have code full of <template>, library uses <ng-template>... What can I do?

You can fork the library in github and modify whatever you need to asure it is compatible with your project. Steps:

  1. Fork library repository and modify what you need
  2. Subscribe to main library repository in order to be updated with changes
  3. In packages.json use your forked version of the library, for example:

"ng2-datetime": "https://github.com/my_user/ng2-datetime/tarball/my_forked_version_name",

  1. If you think that your modifications could suit other users... Make a Pull request! :D
SrAxi
  • 19,787
  • 11
  • 46
  • 65
2

This is more of a TypeScript question since md5 is not an Angular package. The key is to get the import correct to be equivalent to a require() function.

import * as md5 from "md5";

Use directly in TypeScript file:

console.log(md5('message'));

To use this on the template, preferably it should be used in method implementation, but can also be exposed directly. Add it as a property on the Component:

md5: any = md5;

Then on the template:

{{md5('message')}}
George
  • 180
  • 2
  • 9
  • It may be well worth noting that using the syntax `import * as ns from 'module'` to get the `module.exports` value of a CommonJS module is a non standard, TypeScript specific approach that will not work in NodeJS's proposed ES Module interop. – Aluan Haddad Apr 07 '17 at 23:47
1

They usually say which Angular it is meant for, sometimes you have one package for both or for each.

If you are using an Angular 1x package and there is no Angular2 compatibility, then you can just use ngUpgrade.

But If you are using a common plugin then there must be an angular 2 solution.

If you want the other way around then you're probably going the wrong way.

mlk
  • 388
  • 2
  • 10
0

The issue you experienced is not related to Angular. It is an existing issue on TypeScript when importing CommonJS packages.

The rule of thumb (my recommendation) is to stay away from using the interop feature (i.e. import * as X from 'x') when importing CommonJS and use the "old" syntax instead (i.e. import X = require('x')).

When CommonJS is exporting a function in the form of module.exports = function() {...}, import * as X from 'x' does not work.

This includes the case when the package is exporting an ES6 class but transpiling to ES5 with Babel.

You may see some packages do work with this syntax, but that is because the typings have a hack in it:

declare module 'x' {
   function foo(): void
   namespace foo {}  // <-- the hack
   exports = foo
}

There are other reasons the interop is not a good idea, including the syntax between TypeScript (import * X from 'x') and Babel (import X from 'x') does not agree.

You can learn more about it here and follow the links: https://github.com/unional/typescript-guidelines/blob/master/pages/default/modules.md#import-keyword

UPDATE: with TypeScript@2.7 released, you can now do import EditableElement from 'Transformer' directly.

Turn on esModuleInterop in your tsconfig.json

unional
  • 14,651
  • 5
  • 32
  • 56