This article applies to Angular 2 and newer versions.
TL;DR
Using npm link
when developing Angular plugins might give you some headaches when trying to import Angular modules
from your linked package. It's related to the node_modules
directory.
About `npm link`
npm link
makes it easy to use a package you are actively working on in an other project without having to
npm publish
and npm update
with every change. The npm documentation on it is very
clear, basically it just comes down to sym-linking your plugin package into the node_modules folder from the other
project.
The problem when developing Angular modules
I ran into this problem:
VM6012:129 Uncaught Error: Unexpected value 'ExternalModule' imported by the module 'AppModule'
My external Angular module (linked through npm link
) could not be imported by my host application.
Google did not turn up with much useful information on my problem, so I decided to dive into the Angular source code
to get a clue.
Finding out why the external module is invalid
The specific error was thrown by the metadata_resolver class and indicates that Angular could not find the metadata for the module. The surrounding code led me to the ng_module_resolver class, the reflector class and the reflection_capabilities class.
The external module seemed to have the right decorator data, but ng_module_resolve
told me there was not NgModuleMetadata
available for my module.
Stepping into the _isNgModuleMetadata method
showed me that my metadata looked very much like an NgModule
instance, but was actually not.
Diving into the DecoratorFactory method
reminded me that decorators are just functions.
So when the metadata of my module very much looks like an NgModule
(created by the DecoratorFactory
) but in fact
isn't, it might just be an instance of an NgModule
created by another DecoratorFactory
.
From there on it did not take me too long to figure out that the import { NgModule } from "@angular/core";
in my
linked package was using the angular package from the node_modules
folder at it's original location, and not the
ones from the host location.
Wrapping up
In hindsight this all makes sense.
This issue can be explained by the fact that my external package is a symlink and because of
how Node modules are resolved.
So two instances of NgModule
were created, and this explains why the
_isNgModuleMetadata method,
returned false.
Still, it took me quite a while to fully figure this out.
I don't think we can blame Angular for the vague error message or npm link
for the way it's implemented.
It's just a stupid coincidence of symlinks and method instances (decorators).
I stopped using npm link
for this specific project to fix this issue,
i hope this explanation makes sense and can help a few people resolving similar issues when running into them.
The upside
I guess there is mostly always an upside on running into issues like this; I really learnt a lot on Angular 2.
I'm a big believer that knowing the internals of a framework can benefit you when using a framework.