NodeJS - using require works import does not work - node.js

I'm trying to understand why require works while import does not for an npm package. The package is r7insight_node and allows us to send our logs to their product, Rapid7. When we use require as per their instructions things work fine, but don't when I use import.
Their library has an src/index.js file that looks like:
// Use codependency for dynamically loading winston
const requirePeer = codependency.register(module);
// Import winston
const winston = requirePeer('winston', {optional: true});
const Transport = requirePeer('winston-transport', {optional: true});
// If we have successfully loaded winston (user has it)
// we initialize our InsightTransport
if (winston) {
provisionWinston(winston, Transport);
}
// Logger is default export
module.exports = Logger;
// Export as `bunyanStream` to not break existing integration
module.exports.bunyanStream = buildBunyanStream;
module.exports.provisionWinston = provisionWinston;
My understanding is that require is synchronous and is "computed" whereas import is asynchronous and is NOT "computed" as written here. Is this the reason for why require works while import does not?
Does "computed" mean in that the index.js file is executed and hence the if (winston) block is checked and executed in a require but not in an import? Is there a way to achieve the same using import statements?
Thanks in advance

for the package to work with the ES6 import way, it has to be written to it, it has to be exported as eg:export default Logger and not module.exports = Logger, hope my answer helped you

Related

ReferenceError: firebase_app__WEBPACK_IMPORTED_MODULE_0__ is not defined in userFunction (Autodesk's viewer)

My app.bundle.js created by webpack is effing up:
/* harmony import */ var firebase_app__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(10);
/* harmony import */ var firebase_firestore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(16);
var firebaseConfig = {
//config options for database
}; // Initialize Firebase
var defaultProject = firebase_app__WEBPACK_IMPORTED_MODULE_0__.default.initializeApp(firebaseConfig);
var defaultFirestore = defaultProject.firestore();
console.log("Firebase globals: ", firebase_app__WEBPACK_IMPORTED_MODULE_0__.default);
function userFunction(pdb) {
console.log("Firebase globals inside function: ", firebase_app__WEBPACK_IMPORTED_MODULE_0__.default);
}
As you can see firebase_app_WEBPACK_IMPORTED_MODULE_0__ is defined at the top yet when loading webpage error ReferenceError: firebase_app__WEBPACK_IMPORTED_MODULE_0__ is not defined pops up out of nowhere.
Update:
I actually found the mistake, basically I'm using Autodesk's Forge Viewer Library which at some point calls my function userFunction, userFunction needs firebase to process a list in one pass, sadly this function is executed in a webworker (async) which means I can't easily reference a variable outside the function's scope.
You will have to add an import for each Firebase SDK you want to use.
import firebase from 'firebase/app'
import 'firebase/storage'
import 'firebase/analytics'
import ....
If you already have done so, you also need them to be in correct order in a react project
The correct order:
import './initializedFirebase'
import App from './App'
If you import App.jsx before initializeFirebase.js, then firebase.storage() is used before it gets initialized.
Also consider upgrading your version of Firebase which solves many issues.

how can I use npm "debug" package with es modules

this is how I used to used it in common modules.
const debuger=require('debug')("namespace")
I set an environmental variable DEBUG="namespace" and when i start the app, i can use the debugger.
However I could not figure out how to use it with import/export staments.
import debugger from "debug" // how can i pass () here
You can do:
import debug from 'debug';
const logger = debug('namespace');
logger('Starting App');
Actually, as the npm debug module exports a function directly (module.exports = (params) => {...), you can give the function whatever name you like, for example:
import createDebugMessages from 'debug';
const debug = createDebugMessages('namespace');
debug('Starting App');
This way, the original syntax for debugging does not even need to be changed.

Async .mjs works when calling directly, fails when called from another .mjs

I am currently working with the Ring-Client-API and am running into a small issue at the very end of my development. I succesfully created, tested, and ran my RingListener as an individual file, ie by executing RingListener.mjs. My goal is to now start the listener from another file location, and I am running into some issues trying to do that. I am more familiar with CommonJS so please feel free to point me in the right direction for ES6 stuff I am missing. I am running node 14.15.4
Code RingListener.mjs:
import {RingApi} from 'ring-client-api'
import * as dotenv from "dotenv";
dotenv.config({path: '../.env'});
import {readFile, writeFile} from 'fs'
import {promisify} from 'util'
import App from "../objects/GoogleHomeNotification.js";
export async function start() {
const {env} = process;
console.log("Test 1")
const ringApi = new RingApi({my credentials});
console.log("Test 2")
const allCameras = await ringApi.getCameras();
console.log("Test 3")
console.log("Found " + allCameras.length + " camera(s)")
ringApi.onRefreshTokenUpdated.subscribe(
async ({newRefreshToken, oldRefreshToken}) => {
console.log('Refresh Token Updated: ', newRefreshToken)
}
)
if (allCameras.length) {
console.log('Listening for motion and doorbell presses on your cameras.')
}
}
start();
Output for RingListener.mjs
Test 1
Test 2
Test 3
Found 1 camera(s).
Refresh Token Updated: {my token}
Now writing it to proper .env file
Listening for motion and doorbell presses on your cameras.
When I try to start it from my other file, I only reach Test 2.
Start.mjs
import {start} from './objects/RingListener.mjs'
start();
//await start(); //Returns the same results as just start()
Output for Start.mjs
Test 1
Test 2
When running it from another location it seems to get stuck at the first await statement, and I'm not sure why. Any help would be greatly appreciated. I am quite stumped because I am able to actually execute the function and I get the console log statements, but for some reason it keeps failing at the exact same spot with the await call when executed through another file. Is there something I am missing when calling an async function from another file?
Thank you!
EDIT: Thanks #JoshA for pointing me in the right direction for the filepath for dotenv.
The following code now hangs on the "Test 1 Test 2" when I try to import another js module.
import {start} from './objects/RingListener.mjs'
import {default as Webserver} from './app.js'
await start();
Output
Test 1
Test 2
But when I remove my import to the other class it runs perfectly, IE "Test 1, 2, 3, etc".
import {start} from './objects/RingListener.mjs'
//import {default as Webserver} from './app.js'
await start();
Output
Test 1
Test 2
Test 3
Found 1 camera(s).
Refresh Token Updated:
Now writing it to proper .env file
Listening for motion and doorbell presses on your cameras.
I'm not even using it yet and it still is causing it to hang. Eventually I am going to use Webserver.listen(); but the ./app.js just exports the express app.
EDIT: The app.js contains a bunch of variable initialization and express app configuration. Mapping to the different routes on the server. The goal is to remove the app.listen() in the app.js and move it to the Start.mjs and call it by Webserver.listen() from the import.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var logger = require('morgan');
require('dotenv').config()
/* Variable def here */
var app = express();
// app config here
/* Exports */
module.exports = app;
app.listen(1337, () => {
console.log("Starting server on 1337");
})
I assume you are using dotenv to load your credentials from your .env file which you then pass on to the new RingApi({my credentials}) constructor.
If that's the case, the most likely reason it's failing is that dotenv uses fs.readFileSync to read the .env file which looks for files relative to the path where node js was executed and not relative to the path of the module. Which is why it stops working if you execute the app from the Start.mjs which is in a different path.
If you really want to keep the dotenv config call inside your RingListener.mjs file you can rewrite it to something like this which resolves the absolute path for the .env file.
import { resolve } from 'path';
dotenv.config({path: resolve(__dirname, '../.env')});
If you get an error __dirname is not defined this is because it's not available in ECMAScript modules as documented here.
As a workaround, you can do something like this.
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
// Initialize __dirname
const __dirname = dirname(fileURLToPath(import.meta.url));
// Then use it to resolve path for .env
dotenv.config({path: resolve(__dirname, '../.env')});

How do I import into the server.js of expressjs

When i try to import such as like this in server.js:
import SalaryService from '/components/utils/SalaryService';
and then want to use it like this:
const salaryService = new SalaryService();
res.send(salaryService.getSalaries('test'));
However I get the following error message when I try start my dev server:
C:\projects\project\server.js:10
import SalaryService from '/components/utils/SalaryService';
^^^^^^
SyntaxError: Unexpected token import
How could I achieve this?
Nodejs doesn't support ES6 modules syntax
If you want to use ES6 modules syntax you can use babel to run your code
Depends on what module.exports in '/components/utils/SalaryService' is assigned. If it's an object with many functions (or nested objects), meaning the file SalaryService.js exports an object containing a property called 'SalaryService' you can use destructuring like so:
const { SalaryService } = require('/components/utils/SalaryService');
This is nice because you can pick a few properties from a large object cleanly:
const { SalaryService, SomeOtherProp } = require('/components/utils/SalaryService');
Otherwise if the module.exports is assigned the function or object you want directly:
const SalaryService = require('/components/utils/SalaryService');

Can I use a custom module resolution function (like "proxyquire") in place of require() with TypeScript?

I have a TypeScript file config.ts that will be run with node:
import myDependency = require('my-dependency');
export = {
doSomething = () => {
...
}
}
In other TypeScript file, I can import this file with full type safety:
import config = require('./config');
config.doSomething();
config.doSomethingElse(); // compiler error, this method doesn't exist
Now I want to unit test this script. In order to mock out the dependencies that this script require()s I'm using proxyquire, which lets me provide the values that my script will get when it makes calls to require(). Here's what my test might look like:
import proxyquire = require('proxyquire');
const config = proxyquire('./config', {
'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});
expect(config.doSomething()).to.do.something();
This works fine, except that my config variable is of type any because I'm using proxyquire() in place of require(). TypeScript must give the require() function special treatment to allow it to perform module resolution. Is there a way to tell the TypeScript compiler that proxyquire() should also do module resolution, similar to require()?
I could rewrite config.ts as a class or make it use an interface. Then I would be able to explicitly type the variables in my tests by importing the class/interface definition. But allowing proxyquire() to implicitly type things for me would be far be the easier solution.
There is a workaround - you can get the type of config.ts module by importing actual module and using typeof in a type cast:
import proxyquire = require('proxyquire');
import configType = require('./config');
const config = <typeof configType> proxyquire('./config', {
'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});
config.doSomething();
// config.noSuchMethod(); // does not compile
This is not ideal because you have to import the same module in your test twice - the real one just to get at the type of it and "proxiquired" one to actually use in your tests, and you have to be careful not to mix up the two. But it's pretty simple compared to the task of implementing another variant of module resolution for typescript. Also, when configType is used in this way - for typing only - its import will not even appear in generated javacsript code.

Resources