Register Node Module Manually - node.js

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
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"
}
}

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)

Related

Setting up Typescript with expressjs import error

I have a folder structure as following
src
--/lib
----/errors
------/notFound.ts
in notFound.ts, I'm exporting this class
export abstract class HttpError extends Error {
abstract statusCode: number;
constructor(message: string) {
super(message);
Object.setPrototypeOf(this, HttpError.prototype);
}
abstract serializeErrors(): { message: string; field?: string }[];
}
export class NotFoundError extends HttpError {
statusCode = 404;
constructor() {
super('Route not found');
Object.setPrototypeOf(this, NotFoundError.prototype);
}
serializeErrors() {
return [{ message: 'The requested route is not Found' }];
}
}
Then in TSconfig.json, I have the path parameter set like this
"paths": {
"#/lib/*" : [
"src/lib/*"
],
"#/routes/*": [
"src/routes/*"
],
"*": [
"node_modules/*"
]
},
When i Import NotFoundError as follows, it results in error
import { NotFoundError } from '#/lib/errors/notFound';
Here's the error.
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:889:15)
at Function.Module._load (internal/modules/cjs/loader.js:745:27)
at Module.require (internal/modules/cjs/loader.js:961:19)
at require (internal/modules/cjs/helpers.js:92:18)
at Object.<anonymous> (/home/tuser/Projects/tpro/src/server/express.ts:5:1)
at Module._compile (internal/modules/cjs/loader.js:1072:14)
at Module._compile (/home/tuser/Projects/tpro/node_modules/source-map-support/source-map-support.js:568:25)
at Module.m._compile (/tmp/ts-node-dev-hook-07042914060113459.js:69:33)
at Module._extensions..js (internal/modules/cjs/loader.js:1101:10)
at require.extensions..jsx.require.extensions..js (/tmp/ts-node-dev-hook-07042914060113459.js:114:20)
[ERROR] 16:40:04 Error: Cannot find module '#/lib/errors/notFound'
I have also tried imports like these
const { NotFoundError } = require('#/lib/errors/notFound')
const NotFoundError = require('#/lib/errors/notFound')
Both result in same error
What am i missing ?
Sorry, I initially wanted to post a comment but I don't have enough rep; instead, I've expanded what I know into a full answer in hopes it helps.
I've recently gone about with setting up Node + TypeScript + Express. The repo I made and was playing around with initially is here, but note that it's undocumented and the auxiliary configs aren't necessarily up to scratch. Also, apologies if the formatting of this answer is really unorthodox, and I hope linking to personal repos is not looked down on.
I'm uncertain if the paths in the tsconfig.json have anything to do with your issue; I've never used them, so I don't know. I would remove them for now.
As a preface, this is an imperfect solution.
For my Node/TypeScript/Express setup I had to do the following:
Set "type": "module" in package.json
Install required packages: > npm install ts-node ts-loader (I'm unsure if both are necessary, I believe they are though)
Add node --es-module-specifier-resolution=node --loader ts-node/esm ./src/server/express.ts (Presuming that express.ts is your entrypoint) as a command to your package.json and check if it works.
With what I understand, this works by specifically invoking ts-node with esm support, and it seems to work on Node 14 LTS and v16.9.1.
The command is rather hacky and will give you warnings about it being an experimental feature. I couldn't find any other solution for myself when (very stubbornly) using ESModules.
I've also used this command to use Webpack with TypeScript and ESM (that is, having a webpack.ts file with ESM imports) to compile TypeScript src files with ESM, as below (in package.json, from here):
"webpack-examples": "node --es-module-specifier-resolution=node --loader ts-node/esm node_modules/webpack-cli/bin/cli.js --config webpack.examples.ts --mode production",
expressjs doesnt support import statements yet. use require for eg const http = require("http");

Why does short-hand assignment not work in my babel setup?

Background
I am currently in the process of migrating the backend of my project to ES6 via babel and am running into an error that seems to be related to a short-hand assignment that is not working anymore? I am not familiar with babel. Might be that I am not seeing something super obvious here.
Problem
now when trying to run my app in dev via yarn start I am seeing the following error (simplified example) this did work prior to setting up babel (I am on node.js 13.13.0). Any idea what's going on here?
Codesnippet:
const a = {
hi:1, bye:2, ciao: 3
}
const b = {hi, bye} = a
console.log(b)
Errormessage:
[nodemon] restarting due to changes...
[nodemon] starting `NODE_ENV=development babel-node server.js server.js`
/Users/user/Projects/fb/fb-flow-app/server/config/config.js:21
var b = (_a = a, hi = _a.hi, bye = _a.bye, _a);
^
ReferenceError: hi is not defined
at Object.<anonymous> (/Users/user/Projects/fb/fb-flow-app/server/config/config.js:12:8)
at Module._compile (internal/modules/cjs/loader.js:1123:30)
at Module._compile (/Users/user/Projects/fb/fb-flow-app/server/node_modules/pirates/lib/index.js:99:24)
at Module._extensions..js (internal/modules/cjs/loader.js:1143:10)
at Object.newLoader [as .js] (/Users/user/Projects/fb/fb-flow-app/server/node_modules/pirates/lib/index.js:104:7)
at Module.load (internal/modules/cjs/loader.js:972:32)
at Function.Module._load (internal/modules/cjs/loader.js:872:14)
at Module.require (internal/modules/cjs/loader.js:1012:19)
at require (internal/modules/cjs/helpers.js:72:18)
at Object.<anonymous> (/Users/user/Projects/fb/fb-flow-app/server/server.js:1:14)
[nodemon] app crashed - waiting for file changes before starting...
High-level project structure:
root
|-frontend
|-server
The frontend is a next.js project and has it's own .babelrc which seems to be working.
Steps taken so far
I've installed a couple of babel-dev-dependencies (in my server folder):
"devDependencies": {
"#babel/cli": "^7.10.1",
"#babel/core": "^7.10.2",
"#babel/node": "^7.10.1",
"#babel/preset-env": "^7.10.2",
...
}
and also tried (with no result)
#babel/plugin-transform-shorthand-properties
#babel/plugin-transform-spread
I've configured my nodemon.json:
{
"watch": ["../server"],
"exec": "NODE_ENV=development babel-node server.js",
"ext": "js"
}
I've set up a .babelrc
{
"presets": ["#babel/preset-env"]
}
And then I am running via:
"scripts": {
"start": "concurrently --prefix none \"cd server && NODE_ENV=development yarn nodemon server.js\" \"cd frontend && yarn dev\""
},
When you do the following:
const a = {
hi:1, bye:2, ciao: 3
}
const b = {hi, bye} = a
Babel sees an assignment to two variables, named hi and bye that don't yet exist. It seems that Babel is operating in strict mode where this is an error. You'll get the same in Node while in strict mode. Solve this by defining them explicitly:
const a = {
hi:1, bye:2, ciao: 3
}
let hi, bye;
const b = {hi, bye} = a
Destructuring does not make an object, despite it looking like an object initialization.
Note how, because of that, the value of b is not an object that looks like {hi:1, bye:2} but a reference to a, just like what would happen if you just do const b = a:
const b = a;
a.hi = 5;
console.log(b.hi); // 5
Other things you can do:
const {hi, bye} = a; to get two const variables named hi and bye (rather than var or let)
const {hi: b, bye: c} = a; to get two variables named b and
c ("renaming")
const {hi, bye} = a; const b = {hi, bye}; to make an object with just those two keys in variable b, but this will also create variables hi and bye.

node throw error when use async/await syntax.but it works well with import/export syntax

nodemon throw the error when use async/await syntax:
**/node_modules/#babel/runtime/helpers/esm/asyncToGenerator.js:17
export default function _asyncToGenerator(fn) {
^^^^^^
SyntaxError: Unexpected token export
but it works well with import/export syntax.
package.json
{
"scripts": {
"dev": "nodemon --exec babel-node server/index.js",
}
"dependencies": {
"#babel/polyfill": "^7.2.5",
},
"devDependencies": {
"#babel/cli": "^7.2.3",
"#babel/core": "^7.2.2",
"#babel/node": "^7.2.2",
"#babel/preset-env": "^7.2.3",
}
}
.babelrc
{
"presets": [
"#babel/preset-env"
]
}
asyncToGenerator.js
function asyncGeneratorStep(...) { ... }
export default function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
But I think it probably goes wrong because of my babel setting.
BTW, when I use typeof, it throws the same error
**/node_modules/#babel/runtime/helpers/esm/typeof.js:3
export default function _typeof(obj) {
^^^^^^
SyntaxError: Unexpected token export
update 6/12, 2020:
structure:
src (vue app)
server (express app)
|---- src
|---- babel.config.js
|---- index.js
package.json
babel.config.js
In this project, I have two babel config, one is for the vue's app, and another is for the express. What I want is running these apps at the project's root path.
And in the beginning, my script about running express is
nodemon --exec babel-node server/index.js
It can run express, but it gets the wrong babel config(project/babel.config.js)
And the solution is just to point out the specific path which babel confg you want to use(project/server/babel.config.js). So the correct script to run the express is
nodemon --exec babel-node --config-file ./server/babel.config.js server/index.js",
answering your comment:
nodemon doesn't know about babelrc (and it shouldn't). And babel (AFAIK) doesn't allow you to select the babelrc file that you want to use.
I think that you should merge your babelrc files and set the env flag when running babel-node. Like this: babeljs.io/docs/en/6.26.3/babelrc#env-option
Another option would be to make a script that renames the babelrc file each time the app is reloaded, or something like that (I don't understand why you need 2 .babelrc files)
In an answer no longer visible (probably deleted by a moderator) I read that there are more .babelrc files in the project.
From babel docs it seems that the .babelrc needs to be in the same directory of the subpackage. I suggest you to read that doc, probably you can find the solution that better fits your requirements.
Sorry for the vague answer, but due to the lack of details in your question (server/index.js file content, directories structure, etc.) I can't do better.

Zeit/pkg: Cannot find module 'config'

I've distilled this issue down to a simple test. I'm using the node "config" module to define configuration values for my app. Pkg doesn't complain on build, but barfs at runtime with the following message. Am I missing something?
jim-macbookpro:~/development/node/pkgtest$ ./pkgtest-macos
pkg/prelude/bootstrap.js:1172
throw error;
^
Error: Cannot find module 'config'
1) If you want to compile the package/file into executable, please pay attention to compilation warnings and specify a literal in 'require' call. 2) If you don't want to compile the package/file into executable and want to 'require' it from filesystem (likely plugin), specify an absolute path in 'require' call using process.cwd() or process.execPath.
at Function.Module._resolveFilename (module.js:540:15)
at Function.Module._resolveFilename (pkg/prelude/bootstrap.js:1269:46)
at Function.Module._load (module.js:470:25)
at Module.require (module.js:583:17)
at Module.require (pkg/prelude/bootstrap.js:1153:31)
at require (internal/module.js:11:18)
at Object.<anonymous> (/snapshot/pkgtest/index.js:1:78)
at Module._compile (pkg/prelude/bootstrap.js:1243:22)
at Object.Module._extensions..js (module.js:650:10)
at Module.load (module.js:558:32)
index.js is simple:
const config = require('config');
console.log('yo:', config.message);
and I have a default.json in the local 'config' directory:
{
"message": "whodapunk?"
}
My package.json, for what it's worth:
{
"name": "pkgtest",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"config": "^1.30.0"
},
"bin": "index.js"
}
I had the same issue and it was because my config file was added to .gitignore -- as soon as I removed it from there it worked like a charm!
I just had the same issue. However not a really beautiful solution I managed to find a way to work around it.
When calling pkg I do not set e NODE_ENV. Then in my entry point I check if a NODE_ENV is set. If not I define the variables I need there.
let port = null;
let db = null;
let name = null;
if (process.env.NODE_ENV) {
const config = require('config');
port = config.get('port');
db = config.get('database');
name = config.get('name');
} else {
port = 3000;
db = 'mongodb://localhost:27017/production';
name = 'Server Production';
}
I tried linking directly to the config module but after that it started complaining that it could not find and files in my config folder. This worked for me as a work around.

TypeError when mocking request with jest

I'm relatively new to node and am having issues trying to mock request using jest.
If my file to be tested has require('request'), and I try to run npm test, I get this error:
FAIL __tests__/sum-test.js (0.291s)
● sum › it adds 1 + 2 to equal 3
- TypeError: The super constructor to `inherits` must have a prototype.
at Object.exports.inherits (util.js:756:11)
at Object.<anonymous> (node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js:44:6)
at Object.<anonymous> (node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js:16:18)
Here's my package.json, if that helps:
{
"name": "jesttest",
"version": "1.0.0",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest-cli": "^12.0.2"
},
"dependencies": {
"request": "^2.72.0"
}
}
Anyone know why this might be happening?
add jest.unmock('request') in your test file.
Jest will mock a fake require object when you require something in your file.In this situation, request is not the real request.So tell jext not mock request.

Resources