I am trying to create npm library with one directive and test it locally with the help of npm link.
But the problem is when I am including my directive in the declarations array I am getting this error:
Unexpected value 'MyDirective' declared by the module 'AppModule'
My library structure:
package.json
{
"name": "my-directive",
"main": "./dist/index.js",
"typings": "dist/index.d.ts"
}
My src folder:
index.ts
export * from "./myDirective";
myDirective.ts
import {Directive} from '#angular/core';
#Directive({
selector: "my-directive"
})
export class MyDirective {
constructor() {
console.log('directive works!');
}
}
Now in my Angular 2 app that I'm linking this npm package:
import { MyDirective } from "my-directive";
// This line indeed log the constructor function...
console.log(MyDirective);
// function MyDirective() {
// console.log('directive works!');
// }
#NgModule({
declarations: [
AppComponent,
MyDirective
]
})
What am I missing?
I was facing this issue and I don't know exactly why, but in the package.json file, remove the Angular 2 dependencies from the dependencies field to devDependencies solve this problem and my npm module worked.
You should give it a try.
There is a discussion and a PR regarding similar issues here:
https://github.com/angular/angular-cli/pull/2291
See if this might help.
Related
I'm trying to build an Angular wrapper for a Javascript library but I'm getting a "Module not found" error. The Javascript library is in development and not yet published to NPM, so I'm using npm-link which might be causing the problem, but I'm not sure. My Angular version is 8.2.2.
Javascript source library
sourcelib/index.js:
var sourcelib = (function () {
var doSomething = function() {
};
return {
doSomething: doSomething
};
})();
export default sourcelib;
The sourcelib directory also contains package.json and README.md files. An npm-link is created as you would expect:
cd sourcelib
npm link
Angular wrapper
Here are the Angular CLI commands I'm executing to create the Angular workspace, library and test application:
ng new app-test --create-application=false
cd app-test
ng generate library testlib
ng generate application testlib-app
Now link testlib to the Javascript library:
cd projects/testlib
npm link sourcelib
Generate a module and component inside testlib:
cd src/lib
ng generate module test
ng generate component test/testcomp
testcomp.component.ts:
import { Component, OnInit } from '#angular/core';
import sourcelib from 'sourcelib';
#Component({
selector: 'testcomp',
template: '<div></div>'
})
export class TestcompComponent implements OnInit {
constructor() { }
ngOnInit() {
sourcelib.doSomething();
}
}
public-api.ts:
export * from './lib/test/test.module';
export * from './lib/test/testcomp/testcomp.component';
Now build sourcelib:
ng build
Here is the console output:
Building Angular Package
------------------------------------------------------------------------------
Building entry point 'testlib'
------------------------------------------------------------------------------
Compiling TypeScript sources through ngc
Bundling to FESM2015
Bundling to FESM5
Bundling to UMD
WARNING: No name was provided for external module 'sourcelib' in output.globals – guessing 'sourcelib'
Minifying UMD bundle
Copying declaration files
Writing package metadata
Built testlib
------------------------------------------------------------------------------
Built Angular Package!
- from: /Users/.../app-test/projects/testlib
- to: /Users/.../app-test/dist/testlib
------------------------------------------------------------------------------
And create an npm-link to sourcelib:
cd ../../dist/testlib
npm link
Angular test app
Link testlib-app to testlib:
cd ../../projects/testlib-app
npm link testlib
app.module.ts:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { TestcompComponent } from 'testlib';
#NgModule({
declarations: [
AppComponent, TestcompComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts:
import { Component } from '#angular/core';
import { TestcompComponent } from 'testlib';
#Component({
selector: 'app-root',
template: '<testcomp></testcomp>'
})
export class AppComponent {
}
Serve the app:
ng serve
Result
ERROR in /Users/.../app-test/dist/testlib/fesm2015/testlib.js
Module not found: Error: Can't resolve 'sourcelib' in '/Users/.../app-test/dist/testlib/fesm2015'
ℹ 「wdm」: Failed to compile.
I've spent a lot of time troubleshooting this but have not been able to find a solution. Any help would be appreciated.
Angular sometimes has problems building with linked modules. Usually this can be fixed by adding "preserveSymlinks": true to your angular.json under projects.yourProject.architect.build.options. For example:
{
"projects": {
"yourProject": {
"architect": {
"build": {
"options": {
"preserveSymlinks": true
}
}
}
}
}
}
I have a popups module that I wanted to lazy load outside of the regular router lazy-loading so I have done the following
angular.json
"architect": {
"build": {
...
"options": {
...
"lazyModules": ["src/popup-components/popups.module"],
...
}
}
}
Added the module to the lazyModules in the angular.json so angular knows to bundle it seperately
popups.module
import { NgModule } from '#angular/core';
...
#NgModule({
imports: [CommonModule],
declarations: [
...
],
entryComponents: [
...
]
...
})
export default class PopupModule {}
app.component
#ViewChild('entry', { read: ViewContainerRef, static: true }) entry: ViewContainerRef
...
constructor(
private _loader: NgModuleFactoryLoader,
private _injector: Injector
) { }
...
// takes the popup compenent to render as an argument
loadModule(popupComponent) {
this._loader.load('src/popup-components/popups.module').then(moduleFactory => {
const moduleRef = moduleFactory.create(this._injector).instance;
const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(popupComponent);
this.entry.createComponent(compFactory);
});
}
now when I run ng serve all this works as expected, no errors or anything... but when I run ng build --prod I get the following errors
ERROR in ./src/popup-components/popups.module.ngfactory.js 58:64-79
"export 'PopupsModule' (imported as 'i1') was not found in './popups.module'
ERROR in ./src/popup-components/popups.module.ngfactory.js 58:8885-8900
"export 'PopupsModule' (imported as 'i1') was not found in './popups.module'
ERROR in ./src/popup-components/popups.module.ngfactory.js 58:8902-8917
"export 'PopupsModule' (imported as 'i1') was not found in './popups.module'
Now if you noticed above in the popups.module I am using export default class instead of the regular export default If I remove default my app builds fine running ng build --prod but the dynamic loading of the module no longer works I get this error when I try and run the loadModule() method
console.js:35 ERROR Error: Uncaught (in promise): Error: Cannot find 'default' in 'src/popup-components/popups.module'
I'm not sure what to do from here I cant find any resources of a similar issue
Any help would be appreciated!
Okay so I was actually able to solve this issue heres what I did:
In my loadModule() function in the .load() section I needed to do the following
this._loader.load('src/popup-components/popups.module#PopupsModule)
so this looks similar to the old (pre Angular 8) way of lazy loading routing components so the # is used to denote where our file path ends and to tell the load function the name of the PopupsModule
then I also had to change
const moduleRef = moduleFactory.create(this._injector).instance;
to
const moduleRef = moduleFactory.create(this._injector);
and now the project builds fine using ng build --prod
When I build my Angular library, publish it to npm, and use it as a dependency in another project, whenever I try to import on of my module classes into my app.module.ts, and get this error Class TekButtonModule is not an Angular module. I have followed steps from multiple different sites on how to create, build, and publish angular libraries, and I can't figure out why it won't recognize the class as a valid module class.
In my app.module.ts, this is how I am importing the module:
import { TekButtonModule } from "tek-angular-uimodules";
#NgModule({
imports: [
TekButtonModule
]
})
export class AppModule { }
My library project follows the standard directory structure outlined by Angular. I did not change anything with the build, or path settings when I setup the library project. My built library project has a bundles, esm5, esm2015, fesm5, fesm2015, lib (where my custom modules, and components are), as well as a package.json, public-api.d.ts (exports everything in the lib directory), README.md, tek-angular-uimodules.d.ts (exports the public api), and a tek-angular-uimodules.metadata.json file.
Is there some extra configuration that it needed that isn't setup by default to get modules to work correctly?
Here is my button module class:
import { CommonModule } from "#angular/common";
import { NgModule } from "#angular/core";
import { TekButton } from "./button";
#NgModule({
declarations: [
TekButton
],
exports: [
TekButton
],
imports: [
CommonModule
]
})
export class TekButtonModule {}
Here are some of the files that are generated when I build the project:
tek-angular-uimodules.d.ts:
/**
* Generated bundle index. Do not edit.
*/
export * from './public-api';
public-api.ts
export * from "./lib/button";
export * from "./lib/button-group";
export * from "./lib/nav-bar";
./lib/button/button-module.d.ts
export declare class TekButtonModule {
}
If I import the generated javascript module file from the esm5 directory manually in my app.module.ts file, then it works just fine. But I have to manually do that, when it should just work with the standard module import that WebStorm auto imports for me like any other package.
This is the generated module js file under the esm5 directory:
/**
* #fileoverview added by tsickle
* #suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { CommonModule } from "#angular/common";
import { NgModule } from "#angular/core";
import { TekButton } from "./button";
var TekButtonModule = /** #class */ (function () {
function TekButtonModule() {
}
TekButtonModule.decorators = [
{ type: NgModule, args: [{
declarations: [
TekButton
],
exports: [
TekButton
],
imports: [
CommonModule
]
},] }
];
return TekButtonModule;
}());
export { TekButtonModule };
//# sourceMappingURL=data:application/json;base64
Any help is appreciated. I can provide more code, and screenshots if needed.
This is the error I get when trying to import my module:
So I figured out what was causing my specific issue. It was two things. Point two is the direct reason for my issue, point one was making much more confusing to debug.
In order to test my built package, I would run ng build, and then cd into the built project in the dist directory, and run npm pack. I would then install my built package as a file dependency in an entirely separate project to ensure I did everything correctly. What I found out was (or I'm assuming) that there was a caching mechanism going on even when doing local file dependency installations. This caching seemed to be tied to the file name of the tgz file that is generated from npm pack. This caching was causing the very first version to be constantly reinstalled not matter how many changes I made to the built library. The fix, for me, was to simply rename the tgz file to something different each time. You should also change the library version number, and that should also work.
My file structure looked like this:
src
- public-api.ts
- lib
-- button
--- index.ts
--- public-api.ts
--- button.component.ts
--- button.module.ts
-- another-module
-- yet-another-module
The public-api.ts file directly in src looked like this:
export * from './lib/button'
// Other exports
The public-api.ts, and the index.ts file under src\lib\button looked like this:
// public-api.ts
export * from './button.component.ts'
export * from './button.module.ts'
// index.ts
export * from './public-api.ts'
I had assumed that by adding the index.ts files to each directory, the export line in the root public-api.ts would find the index file, and export the files accordingly, but somewhere along the line, this implicit resolution does not work, and, instead, an explicit export is needed. The fix was to change export * from './lib/button' to export * from './lib/button/index.ts' or export * from './lib/button/public-api.ts'.
Point two was the direct reason for my issue, point one just made it really confusing when trying to debug.
So I had the same issue with a different solution, so I thought I would share it here.
I had just updated my library to Angular 9. And everything seemed fine except the strange "Class my-module is not an Angular module" error.
What worked for me was to up tsconfig.lib.ts file with:
...
"angularCompilerOptions": {
...,
"enableIvy": false
}
Don't know if this will help anyone, but for me it was caused by the NgModule import path autocompleting with #angular/core/src/metadata/*, rather than #angular/core.
Is it possible to use JSON Schema faker as a third party dependency in Angular. I tried to use the dependency injection to Angular however in the providers I am not able to import jsonSchemaFaker.
angular.json
"scripts": [
"./node_modules/json-schema-faker/dist/json-schema-faker.bundle.min.js"
]
jsonSchemaFaker.service.ts
import { InjectionToken } from '#angular/core';
export const JSF_Token = new InjectionToken ('jsonSchemaFaker');
app.module.ts
providers: [
{ provide: JSF_Token, useValue: jsf }
]
...
declare let jsf: any;
This is what I tried to Inject json schema faker as a dependency in my angular app.. I am getting .. Uncaught ReferenceError: jsf is not defined
That is not how you use npm packages in an angular application.
First off, navigate to the directory of your package.json in your application and install the package:
npm install json-schema-faker
Then, inside your components or your services, use it as such:
// at the top of your file, next to other imports
import jsf from 'json-schema-faker';
// json-schema-faker is now available in the rest of your file as jsf
// you can, for example, have a service method that returns that:
jsf.generate({type: 'string'})
I had to change my providers and the declaration to
providers: [
{ provide: JSF_Token, useValue: JSONSchemaFaker }
]
declare let JSONSchemaFaker: any;
Reason: the global name for JSON Schema Faker mentioned in that library is "JSONSchemaFaker". It was a mistake on my part to declare it as jsf.
I'm trying to use Angular 1.x with TypeScript 1.5.3 and SystemJS. The index.html page is set up to System.import('bootstrapper') which should start things up.
bootstrapper.ts gets compiled to bootstrapper.js and works fine as long as it doesn't use angular (i.e. doing just a console.log() works ok)
Now, I'd like to import and use angular to bootstrap it. I've already done jspm install angular and I also installed some typings for angular using tsd. The typings are referenced at the top of the bootstrap.ts file.
Unfortunately doing import angular from 'angular' doesn't compile, I get Module "angular" has no default export. My questions are:
Why doesn't import angular from 'angular' compile? Looking in the angular.d.ts file I see declare module 'angular'{ export = angular; } which, if I understand correctly, is a default export from the module angular of a variable (defined above in the typings file) declare var angular: angular.IAngularStatic
I noticed that doing import 'angular' compiles and then I can actually reference angular and do e.g. angular.module(...), but I don't think I understand correctly how this works. Shouldn't import 'angular' do a "bare import", i.e. running a module only for its side effects? If that's the case, does that mean that this import actually registers angular in the global scope?
I'm pretty sure I don't understand correctly how modules/type definition files work in Typescript, thanks in advance for an explanation.
Firstly, the following is my preferred way to use AngularJS 1.5 with TypeScript and SystemJS:
index.html
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
SystemJS.import('app')
.then(function (app) {
app.bootstrap(document);
})
.catch(function (reason) {
console.error(reason);
});
</script>
app/main.ts
import angular from 'angular';
const app = angular.module('app', []);
export function bootstrap(target: Element | Document) {
angular.bootstrap(target, [app.name], { strictDi: true });
}
tsconfig.json
{
"compilerOptions": {
"module": "system",
"allowSyntheticDefaultImports": true,
....
}
}
config.js (loader config, simplified)
SystemJS.config({
transpiler: "typescript",
packages: {
"app": {
"main": "main.ts",
"defaultExtension": "ts",
"meta": {
"*.ts": {
"loader": "typescript"
}
}
}
}
});
Notes:
If you are using JSPM 0.17 specify "plugin-typescript", not "typescript" for the loader and transpiler or just run $ jspm init and select a transpiler.
The you can import angular as a default, but it will still register itself on the global scope.
The reason you are getting syntax errors is because angular, and its angular.d.ts declaration file contains a CommonJS style export = declaration to allow for module and global namespace based usage. SystemJS, for maximum compatibility, interpolates this into a default export at runtime.
The TypeScript compiler needs to be made aware of this. This can be done by setting "module": "system", and/or specifying "allowSyntheticDefaultImports": true. I have done both for the sake of exposition and explicitness.
If you aren't using jspm, you just need to tell system JS where to find the typescript using map or package config
SystemJS.config({
map: {
"typescript": "node_modules/typescript"
}
});
[...] if I understand correctly, is a default export from the module
angular of a variable
Nope, that's not what's happening. Angular exports the entire namespace as the export, if that makes sense.
import angular from 'angular' is attempting to import the default from the module.
What you want is import * as angular from 'angular'; which imports the entire export as a variable.