AryanJ-NYC
AryanJ-NYC

Reputation: 2667

Importing node modules with TypeScript, Angular 2 and SystemJS

I am writing a basic Angular 2 app. I am creating a class User that has a function validPassword() that uses bcrypt to validate a password:

import { compareSync, genSaltSync, hashSync } from 'bcrypt';
import { Document, Schema, model } from 'mongoose';

export class User {
  username: string;
  password: string;

  isValidPassword(password: string): boolean {
    return compareSync(password, this.password)
  }
}

let userSchema = new Schema({
  username: { required: true, type: String },
  password: { required: true, type: String }
}, { timestamps: true });

userSchema.pre('save', function (next) {
  this.password = hashSync(this.password, genSaltSync(8));
  next();
});

export interface UserDocument extends User, Document {}

export const Users = model<UserDocument>('User', userSchema);

As you see, I am using the bcrypt and mongoose npm packages.

My SystemJS is configured as follows:

(function (global) {
  System.config({
    map: {
      '@angular': 'node_modules/@angular',
      'bcrypt': 'node_modules/bcrypt',
      'bindings': 'node_modules/bindings',
      'mongoose': 'node_modules/mongoose',
      'rxjs': 'node_modules/rxjs'
    },
    paths: {
      'node_modules/@angular/*': 'node_modules/@angular/*/bundles'
    },
    meta: {
      '@angular/*': {'format': 'cjs'}
    },
    packages: {
      'src': {main: 'main', defaultExtension: 'js'},
      '@angular/core': {main: 'core.umd.min.js'},
      '@angular/common': {main: 'common.umd.min.js'},
      '@angular/compiler': {main: 'compiler.umd.min.js'},
      '@angular/forms': {main: 'forms.umd.min.js'},
      '@angular/http': {main: 'http.umd.min.js'},
      '@angular/platform-browser': {main: 'platform-browser.umd.min.js'},
      '@angular/platform-browser-dynamic': {main:'platform-browser-dynamic.umd.min.js'},
      'bcrypt': {main: 'bCrypt.js'},
      'mongoose': {main: 'index.js'},
      'rxjs': {defaultExtension: 'js'}
    }
  });
}(this));

My typings dependencies are:

{
  "globalDependencies": {
    "bcrypt": "registry:dt/bcrypt#0.0.0+20160316155526",
    "core-js": "registry:dt/core-js#0.0.0+20160914114559",
    "jasmine": "registry:dt/jasmine#2.5.0+20161003201800",
    "mongodb": "registry:dt/mongodb#2.1.0+20160602142941",
    "mongoose": "registry:dt/mongoose#4.5.9+20161010180758",
    "node": "registry:dt/node#6.0.0+20161014191813"
  }
}

My tsconfig is as follows:

{
  "compilerOptions": {
    "baseUrl": ".",
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  }
}

When I go to run the project, the following errors display in the console:

1274 GET http://localhost:8080/crypto 404 (Not Found)
http://localhost:8080/node_modules/mongoose/lib.js 404 (Not Found)
http://localhost:8080/node_modules/bindings/ 404 (Not Found)

I understand this is a SystemJS issue. When I go to add, say,

'bindings': 'node_modules/bindings'

to the map object and,

'bindings': {main: 'bindings.js'},

to the packages object in SystemJS, I get a whole new set of errors (cannot find fs and path).

Should I be manually adding all missing package paths each time I install a new package? Is there a better way to do this?

Upvotes: 0

Views: 4154

Answers (2)

Baseless
Baseless

Reputation: 81

I was stuck with the same problem for some time, and I was using Angular-cli so guess no SystemJs involved. I was trying to load 'diff' node module, and following worked for me:

import * as JsDiff from 'diff';

Source: https://medium.com/@s_eschweiler/using-external-libraries-with-angular-2-87e06db8e5d1

Hope it helps!

Upvotes: 0

Atheteo
Atheteo

Reputation: 43

I recently had the same issue when bundling my server-side into one file with SystemJS. The problem, at least with mongoose (and I suspect the others), is that SystemJS does not support node extension format so it doesn't understand to look for index.js inside the "./lib" folder. And even if you add "mongoose/lib" to your System.config, mongoose depends on a slew of node modules that would require an obnoxious amount of mapping in your System.config.package.mongoose to compensate.

A solution that I've found is to create a new System module and set it to the "package" that you need, above your System.config.

System.set('package', System.newModule({
  default: require('package') 
}));

Unfortunately if you're using typescript your import statements require your "package" to have a type. These are basically just a set of interfaces and abstract classes so that your entire typescript project can understand the "package" library has functions, classes, etc...

But some package types, like mongoose, do not have a default export. So if you set the mongoose module like above and import like below..

import mongoose from 'mongoose';

you are sure to get either transpiler or IDE typescript errors because as said above the typings type does not have a corresponding default export.

To fix this just set allowSyntheticDefaultImports option to true in your tsconfig.json

Hope this helps :)

Upvotes: 1

Related Questions