Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Register Node Module Manually

Question:

I have a project in TypeScript that uses several APIs I don't have access to on my computer (they exist on the web). The code will compile fine locally since I have all the APIs in foo.d.ts files, and so the system knows they exist somewhere.

However, I want to unit test parts of the code with a NodeJS app. I can import the code into node just fine, but whenever I reach code that imports a module from a definition file, I get the following error:

Error: Cannot find module 'messages'
    at Function.Module._resolveFilename (module.js:527:15)
    at Function.Module._load (module.js:476:23)
    at Module.require (module.js:568:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (~/dev/repos/sample_typescript_fail/App.js:3:18)
    at Module._compile (module.js:624:30)
    at Object.Module._extensions..js (module.js:635:10)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)
...

This makes sense, since that code is just defined locally, and does not exist.

Can I manually register modules to NodeJS, like

 Registry.register('messages', () => {...});

so that I can compile and test with polyfills?


Here's an example app

package.json

{
  "name": "sample_typescript_declare_issue",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "start": "ts-node index.ts"
  },
  "author": "",
  "license": "MIT"
}

index.ts

import {App} from "./App";

console.log("Starting program");

// How do I fake "import {MessageSender} from "messages";"
// here so that I can run this node app as a test?

let app: App = new App();

console.log("Ending program");

App.ts

import {MessageSender} from "messages";

export class App {
    constructor() {
        let messageSender: MessageSender = new MessageSender();
        messageSender.sendMessage("foo!");
    }
}

node_modules/@types/messages/index.d.ts

export = Messages;
export as namespace Messages;

declare module Messages {

    class MessageSender {
        constructor();
        sendMessage(message: any): void;
    }

}

Running Example App

Running with npm start gives the error message above.

Running tsc *.tsc compiles just fine.


Other things I've tried

  1. Updating package.json to include a bin:

    {
      "name": "sample_typescript_declare_issue",
      "version": "1.0.0",
      "description": "",
      "main": "index.ts",
      "scripts": {
        "start": "ts-node index.ts"
      },
      "author": "",
      "license": "MIT",
      "bin": {
        "messages": "./polyfills/messages/index.ts"
      }
    }
    
like image 881
Jason Avatar asked Oct 02 '17 21:10

Jason


People also ask

How do I install a node module in NPM?

npm install <your-module-name> In the test directory, create a test.js file which requires your module and calls your module as a method. On the command line, run node test.js. The message sent to the console.log should appear.

How do I run a node test from a module?

In the test directory, create a test.js file which requires your module and calls your module as a method. On the command line, run node test.js. The message sent to the console.log should appear.

What is a node module?

Developer and author at DigitalOcean. The author selected the Open Internet/Free Speech Fund to receive a donation as part of the Write for DOnations program. In Node.js, a module is a collection of JavaScript functions and objects that can be used by external applications.

How to load native modules using node-Gyp?

In order to load native modules on Windows, node-gyp installs a delay-load hook that triggers when the native module is loaded, and redirects the node.dll reference to use the loading executable instead of looking for node.dll in the library search path (which would turn up nothing).


1 Answers

As you mentioned, compiling works fine - this is just a question of availability of .d.ts files.

What you want to do is alter module import at runtime, in other words alter the behaviour of the nodejs require function since

import {MessageSender} from "messages";

will be transpiled in javascript (ES6) to something like

const messages_1 = require("messages");
...
messages_1.MessageSender

To modify that behaviour, the first thing that springs to mind is to use the deprecated - but still available - require.extensions object.

When running locally you must first inject something like

require.extensions['.js'] = (module, filename) => {
    if (filename === 'messages') {
        // then load mock module/polyfill using the passed module object
        // see (https://nodejs.org/api/modules.html#modules_the_module_object)
    }
};

The doc says there are better alternatives but fails to clearly mention any.

Another possibility is to look at projects like sandboxed-module which should help (I have not tested it)

like image 164
Bruno Grieder Avatar answered Oct 24 '22 11:10

Bruno Grieder