0

I'm developing an Electron application with Angular 2.
I followed this tutorial to initially setup the environment.
My setup is a bit more complicated but in general is very similar.
tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
    "node_modules",
    "app/node_modules",
    "dist"
  ]
}

systemjs.config.js is just like in the tutorial as well as index.html

One of my modules (which resides somewhere inside the app folder - app/*/*/*/module) depends on node-ffi. So I've added the necessary typings to typings.json:

{
  "globalDependencies": {
    "core-js": "registry:dt/core-js",
    "jasmine": "registry:dt/jasmine",
    "node": "registry:dt/node",
    "ref": "registry:dt/ref",
    "ref-struct": "registry:dt/ref-struct",
    "ffi": "registry:dt/node-ffi"
  }
}

Now, the module tries to use ffi like this:

import { DynamicLibrary, Library, types } from 'ffi';

export class CppProxy { 
   //Some code
}

Which is eventually transpiled to:

var ffi_1 = require('ffi');
//Utilize ffi_1 exported stuff

According to this article, there is a well-defined way for node.js module lookup and according to this way it should find the node-ffi module as node-ffi resides in app/node_modules. However, it doesn't. It only looks in app folder for ffi.js and obviously fails to find it.
My first attempt to fix it was adding ffi entry to the map section of systemjs.config.js, like this:

  var map = {
    'app': '.', // 'dist',
    '@angular': 'node_modules/@angular',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    'rxjs': 'node_modules/rxjs',
    'node-binary': 'node_modules/systemjs-plugin-node-binary/node-binary.js',
    'ffi': 'node_modules/ffi/lib/ffi.js'
  };

It helped to load ffi, but brought up new problems. ffi itself depends on other modules:

var ref = require('ref')
var assert = require('assert')
var debug = require('debug')('ffi:ffi')
var Struct = require('ref-struct')
var bindings = require('./bindings')

So now the application failed to find these modules. I've tried to add some of them to the map too, but again it just solved one level of dependencies.

It doesn't seem right to me, require shouldn't be looking only in app folder and I don't feel like adding all the dependencies recursively to map section of systemjs.config.js. What am I doing wrong?

Upd:
There another question dealing with a pretty similar issue, but it is asking specifically about using require('remote').
I'm asking how can I use a Node.js module resolution mechanism while still using System.js as a module loader.

Community
  • 1
  • 1
JeB
  • 11,653
  • 10
  • 58
  • 87
  • `SystemJS` is not setup to do transitive dependency configuration. You could use [`jspm`](http://jspm.io/) which will setup `systemjs.config.js` for you or you could wait for [`angular-cli`](https://github.com/angular/angular-cli) to support install or you can use [webpack](https://webpack.github.io/) (which avoids SystemJS entirely). – Pace Aug 03 '16 at 18:14
  • So to make it work with just System.js I have to map all the libraries recursively? I don't get it... I mean, the dependency is specified by require(), which is node.js function... Why isn't it working node.js way? – JeB Aug 03 '16 at 18:27
  • I should have been a bit more clear. SystemJS is transitive, like NodeJS, it is doing the lookup recursively (that is after it finds `ffi` it goes to lookup `ref`). However, SystemJS isn't able to use NodeJS' dependency resolution mechanism (look for node_modules, go up a directory and look for node_modules again, recurse). It can't use that mechanism because websites don't typically copy all of their node_modules into their static distribution directory (your site would be huge!) Front end configuration has always (e.g. `bower`, ` – Pace Aug 03 '16 at 18:35
  • I suspect you're running into one of the problems of a unified `front-end/back-end` package management solution which is that people can unintentionally grab a package that was only ever intended for backend use and try and use it in the browser. I'm not sure `node-ffi` is intended for use in the browser or that it would even be possible. – Pace Aug 03 '16 at 18:38
  • I see... Actually I'm developing an Electron app, so there is no such thing as `front-end/back-end` in the mean of network (everything is running locally). But eventually it is an Angular 2 component that calls to the module which depends on `ffi`. So I assume you're right about the root cause. Could you elaborate about the transitiveness of `System.js`? How does it look for the dependencies? And what's the way to declare those dependencies? – JeB Aug 03 '16 at 18:55
  • I don't know much about Electron. However, whenever a require statement is hit, `System.js` will look in its configuration to see if it knows where to find the requested module. If that module then requires additional modules then the exact same process will happen. So you will need an entry in your map for every single module you want to load. – Pace Aug 03 '16 at 19:23
  • But why it is `System.js` the one that is looking for the modules and not `Node.js`? After all, `require` is a `NodeRequire` function, so why in the world `System.js` would interfere and how exactly is that happening? – JeB Aug 03 '16 at 19:36
  • 1
    I don't really know but given [this comment](https://github.com/systemjs/systemjs/blob/master/dist/system.src.js#L1683) it seems that systemjs replaces node's require when it sets up. – Pace Aug 03 '16 at 20:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/119052/discussion-between-pace-and-meltedspark). – Pace Aug 03 '16 at 21:30
  • Actually...I think [this question](http://stackoverflow.com/questions/34328961/importing-node-modules-with-electron-and-systemjs) has your answer. – Pace Aug 03 '16 at 22:04

1 Answers1

2

As Pace mentioned in one of his comments, System.js overrides Node.js's require method and uses it's own resolution mechanism. This is why the require method won't follow the Node.js lookup mechanism. However there is a way to use the latter:

System.js stores the Node.js's require in _nodeRequire variable. So the way to load a module using Node.js mechanism is to load it by

var module = System._nodeRequire('./path/to/module/')

Here is the discussion that helped me to come up with this solution.

Community
  • 1
  • 1
JeB
  • 11,653
  • 10
  • 58
  • 87
  • Have as look at [my solution](http://stackoverflow.com/a/41351751/322984) inspired by this great answer. – Monsignor Dec 27 '16 at 20:26