Why I can not import directly from node_modules using SystemJS?

256 Views Asked by At

While there is a lot of questions and documentation on SystemJS, I still don't understand the import syntax. Specifically, why can typescript not find ng-boostrap.js using this code:

import { createPlatform } from '../../node_modules/@ng-bootstrap/ng-bootstrap/bundles/ng-bootstrap',

which is directly importing the file, but this code works:

import {createPlatform } from './node_modules/@angular/core/bundles/core.umd.js';

where my map in systemjs.config.js contains the line:

'@angular/core': 'npm:@angular/core/bundles/core.umd.js'.

Why I can not import directly from node_modules using systemJS?

1

There are 1 best solutions below

6
On BEST ANSWER

Note: Though the solution below works, some of the information is incorrect. Please see discussion below in comments.

First of all, TypeScript doesn't know anything about JS files. It knows how to produce them, but doesn't know how to compile against them. So I am not sure how you actually got

import {createPlatform } from './node_modules/@angular/core/bundles/core.umd.js';

to compile in your TypeScript code.

We are able to do

import {createPlatform } from '@angular/core';

in TypeScript, because TypeScript is already looking in the node_modules. And @angular/core, if you look inside your node_module, has the directory @angular/core, with an index.d.ts file. This is the file that our TypeScript code compiles against, not the JS file. The JS file (the one in the above first code snippet) is only used at runtime. TypeScript should know nothing about that file.

Using the second snippet above, when the TypeScript is compiled to JS, it looks like

var createPlatform = require('@angular/core').createPlatform;

As runtime, SystemJS see's this, then looks at the map configuration, and maps the @angular/core to the absolute file location, and is able to load that file

'@angular/core': 'npm:@angular/core/bundles/core.umd.js'

This is the pattern that you should follow with the ng-bootstrap. Use the import that points to the TypeScript definition file, so that it can compile

import { ... } from '@ng-bootstrap/ng-bootstrap';

If you look in the node_modules/@ng-bootstrap/ng-bootstrap directory, you should see the index.d.ts file. This is what TypeScript will use to compile against. When it is compiled to JS, it is compiled the following

var something = require('@ng-bootstrap/ng-bootstrap').something;

And in the SystemJS config, we need to map @ng-bootstrap/ng-bootstrap to the absolute path of the module file, otherwise SystemJS won't know how to resolve it.

'@ng-bootstrap/ng-bootstrap': 'npm:@ng-bootstrap/ng-bootstrap/bundles/ng-bootstrap.js'

One of the key take-aways from this, is to understand the difference between compile-time and runtime. Compile type is TypeScript, which doesn't know anything about JS files, as those are runtime files. SystemJS is the one that needs to know about the runtime (JS) files.