How to dynamically select module with modern nodeJS? - node.js

I am using node --experimental-modules test.mjs (NodeJs v11.9.0).
There are many options to implement the same class, and I need to swith by terminal,
switch (process.argv[2]) {
case 'a':
import MyClass from './MyClass-v1.mjs'; break;
case 'b':
import MyClass from './MyClass-v2.mjs'; break;
default:
import MyClass from './MyClass-v3.mjs';
}
ERROR on import MyClass: Unexpected identifier
PS: working fine when using isolated import MyClass from './MyClass-v3.mjs';.
NOTE: const MyClass = require('./MyClass-v3.mjs') not works with modern Javascript. (ReferenceError: require is not defined). The file have only a class definition,
export default class MyClass { ... }
PS: there are nothing like C_preprocessor for NodeJs? An old pre-compiler (with some argv access) will be fine here.

import ClassV1 from './MyClass-v1.mjs';
import ClassV2 from './MyClass-v2.mjs';
import ClassV3 from './MyClass-v3.mjs';
const classes = {
a: ClassV1,
b: ClassV2
}
const Class = classes[process.argv[2]] || ClassV2;
You could also write an index file with all the classes and do an import * as classes from 'classes.mjs';.

Modern Javascript (ES6+) implementations are under construction, some syntax are working with NodeJS (node --experimental-modules) and good browsers, other not.
This is the only way to do without cost of "load all" (see #Markus solution), but with the cost of non-global import, needs an ugly block to define scope and assyncronous operation... First step is to understand the basic impor block syntax:
import('./MyClass-v3.mjs').then(({default: MyClass}) => {
// can use MyClass only here in this block
let x = new MyClass();
console.log("DEBUG=",x);
});
Them, putting all together, the argv selection and the optional imports.
Solution
const classes = {
a: './MyClass-v1.mjs'
,b: './MyClass-v3.mjs'
,c: './MyClass-v3.mjs'
};
import(classes[process.argv[2]] || classes.c ).then(({default: MyClass}) => {
let x = new MyClass();
console.log("DEBUG=",x);
});
PS: some of the magic (no rational) is to use ({default: MyClass}) instead (MyClass).
Thanks
To #Bergi (!), for explain this solution (see question comments).

Related

node/typescript: how to ensure imports with side effects are run?

I am writing a node app in typescript. I have written a class decorator #myDecorator, and the purpose of #myDecorator is such that I need to keep track of all the classes to which it's applied.
My question: how do I make sure all of those decorated classes are loaded before making use of that behavior? Some example code will help to make this more concrete:
Declaration of the decorator in file myDecorator.ts:
type ConstructorType = { new (...args: any[]): any };
// keep track of decorated classes
const registeredClasses: Map<string, ConstructorType> = new Map();
// class decorator
export function myDecorator<T extends ConstructorType>(targetConstructor: T) {
// create the modified class
const newClass = class extends targetConstructor { /* ... */ }
// register the modified class
registeredClasses.set(targetConstructor.name, newClass);
// and return it
return newClass;
}
export function doSomethingWithMyDecoratedClasses() {
//... some behavior that relies on `registeredClasses`
}
Declaration of a decorated class in file someClass.ts
import {myDecorator} from 'myDecorator.ts'
#myDecorator
class SomeClass { /* ... */ }
Making use of doSomethingWithMyDecoratedClasses in anotherFile.ts:
import { doSomethingWithMyDecoratedClasses } from 'myDecorator.ts`
//...
doSomethingWithMyDecoratedClasses()
//...
The problem is that I need to make sure that SomeClass has been added to registeredClasses before I make this call to doSomethingWithMyDecoratedClasses. And, in fact, I've written a number of such classes in my app, and I need to make sure they are all registered.
My current best understanding is that I need to call import 'someClass.ts' in anotherFile.ts (and, in fact, import all files where decorated classes are declared), so really I need to import someClass1.ts, import someClass2.ts, ...
Is that the best/only approach? Is there a recommended pattern for doing so?
Most applications have an index file that is responsible for importing the top level things. If you import doSomethingWithMyDecoratedClasses there, you'll guarantee that everything else is imported first.
An alternative would be to not call it in the root level of a module, and instead wait for an event.

How to make a ES6 class visible for both the server and client scripts in Node JS?

So, I have 2 classes for shapes in a script that's called shapes.js
class Shape {
constructor(x, y) {
this.x = x;
this.y = y
}
}
class Cube extends Shape {
constructor(x, y, t) {
super(x, y);
this.t = t;
}
}
How do I import both of these in my server.js or other js files? I know Shape is nothing more than an abstract class right now, but I want to test the functionality of importing multiple classes. I have tried doing it in the following ways:
var shape = require('/shapes');
var Shape = shape.Shape, Cube = shape.Cube;
or
import {Shape, Cube} from 'shapes'
import {Shape, Cube} from '/shapes'
I have also tried exporting them in shapes.js at the end of the file like this :
module.exports = {Shape, Cube}
or
export {Shape, Cube}
I've tried all the possibilities I've been provided within the basic tutorials, the result is either an error or a blank white screen with no errors. I have been really stuck on this one, would appreciate some help, thank you
I encourage you to use the ES Module syntax :
import {Shape, Cube} from 'shapes' to import a module
export {Shape, Cube} to export a module
Most browsers support this.Unfortunately! Node.js suppport ES6 but doesn't support ESModule syntax (or only experimental way)
So you need to transpile your code with babeljs and this plugin
Here the .babelrc file to configure babel:
{
"plugins": ["transform-es2015-modules-commonjs"]
}
If you use babel-register the transformation occurs when the file is required(imported)
The module.exports syntax is the best way to export your code. However, the best way to import them differs between Node and browsers.
module.exports = {Shape, Cube}; // in file with classes defined
// below are different ways to import
const { Shape, Cube } = require('./shapes'); // Node
import { Shape, Cube } from './shapes'; // modern browsers
The Node line has something called a destructuring statement. It just prevents you from having to define both shape and Shape/Cube.
You said in your question that you tried both module.exports and a require statement. My guess is that you either a) didn't try both at the same time or b) without the ./ in your require statement, the program couldn't find your file.
Note that the export/import keyword do not work in Node - they work only in modern browsers. As other answers have noted, there are ways to make those work. However, I would generally recommend against them for smaller scale projects, particularly if you're just getting familiar with this stuff.
In my last projects I used Typescript with node.js it's very powerful so I use namespace way :
shapes.ts :
export namespace Shapes {
export class A {
constructor(x, y) {
this.x = x;
this.y = y
}
}
export class B {
constructor(x, y) {
this.x = x;
this.y = y
}
}
}
usage :
import Shapes from 'shapes'
let shapeA = new Shapes.A();

How does Node.js module system handle modifying and accessing variable of the same module of different versions?

I have a custom Node.js module fooModule that has a private variable foo and public getter and setter to modify this variable.
I have another two modules: zooModule depends on the fooModule#^1.0.1 and cannot use pre-release so far and barModule that depends on the fooModule#^1.0.2-0 (pre-release patch version that contains some fix) and zooModule at the same time.
The barModule firstly sets the foo variable value and then zooModule reads the value of foo.
I have noticed that when the version of the fooModule dependency is the same, then it works as expected, in other words the foo variable is shared between two modules. However, using different versions results in undefined when accessing foo from zooModule.
Here is a small pseudo example to demonstrate the logic. Each of the modules is a standalone npm package.
// fooModule.js
let foo;
export const getFoo = () => foo;
export const setFoo = (newFoo) => foo = newFoo;
// zooModule.js uses v.1.0.1 of the fooModule
import { getFoo } from './fooModule.js'
export const zooFunc = () => {
const zoo = getFoo();
if(!zoo) return;
...
return zoo; //result depends on zoo
};
// barModule.js uses v.1.0.2-0 of the fooModule
import { setFoo } from './fooModule.js'
import { zooFunc } from './zooModule.js'
setFoo('foo');
zooFunc(); // What is the output?
As far as I am concerned, in case of different versions of the fooModule, we become two different instances of the module and accordingly of the variable foo?
I tried to explain the question best I could, but it was hard to explain what I mean, sorry if it is still unclear.
Could give me some hints where to read more about this or give some explanation on how this is supposed to work. Thanks for your time.
EDIT:I forgot to mention that I have this use case in a frontend project bundled by a webpack.

Difference between `import from` and `import require` in TypeScript

I use node.js and I recently decided to give TypeScript a shot, But I'm kinda confused on how modules get imported. I see two different syntax that I couldn't find out what's their difference exactly:
import * as a from 'a'; // ES6 standard to import stuff
// OR ...
import a = require('a');
Are these the same thing? and if they're not, where should I use each one of them?
import * as a from 'a'; is the new "ES6 style" import syntax (available since Typescript 1.5).
Whenever possible, this syntax should now be used.
There is one caveat though. The ES6 import syntax can only import modules (as defined by ES6) or objects (classes, interfaces, vars,... ) exported as part of a module.
Some Javascript librairies will directly export a function or class, and the corresponding definition file will typically look like this:
declare module "my-class" {
class MyClass { ... }
export = MyClass
}
In this case, the "old" import syntax is the only one that can be used
import MyClass = require("my-class");
Failure to use this syntax will result in error TS2497
Check this issue for details and a possible workaround which would be, in the previous case, to add an empty module declaration to the definition file
declare module "my-class" {
class MyClass { ... }
module MyClass {} // <=
export = MyClass
}

TypeScript module import in nodejs

What is best practice for importing modules in nodejs with typescript? I come from c# background so I want to do something like this
MyClass.ts
module MyNamespace {
export class MyClass {
}
}
app.ts
// something like using MyNamespace
new MyNamespace.MyClass();
or
MyClass.ts
export class MyClass {
}
app.ts
import MyClass = module("MyClass")
new MyClass();
I know I can do this and it will work, but then I have to think up for two names for each class
import MyClass2 = module("MyClass")
new MyClass2.MyClass();
Point is separating classes to multiple .ts files (preferably one file per class). So question is, how is this done?
You have two choices here:
If you insist on using CommonJS or AMD modules, then you will have to use external modules just the way you described it in your question. Whether or not you use a module to declare your own namespace is mostly a matter of taste. The only way to circumvent the issue of specifying two names is to create a variable that aliases the type:
mymodule.ts
export module MyNamespace {
export class MyClass {
}
}
app.ts
import ns = require('mymodule');
var myclass = new ns.MyNamespace.MyClass();
var myclassalias = ns.MyNamespace.MyClass;
var myclass2 = new myclassalias();
Your other option is to use internal modules which are mostly used to structure your code internally. Internal modules are brought into scope at compile time using reference paths.
mymodule.ts
module MyNamespace {
export class MyClass {
}
}
app.ts
///<reference path='mymodule.ts'/>
var myclass = new MyNamespace.MyClass();
I think you'll have to decide for yourself which of those two approaches is more appropriate.
You can import TypeScript modules into a node.js file using the typescript-require module, which was created for this specific purpose.
I would recommend against using the explicit module (or namespace) keyword, it's a vestigial remnant of an earlier time.* You generally don't need them because any typescript file with a top-level import or export is automatically a module. Your second myModule.ts example was good.
export class MyClass {
...
}
But when you import it to another typescript module, you'll want to use something like this:
import { MyClass } from './myModule';
var myInstance = new MyClass();
Personally, I don't like repetitiveness of line 1, but it is what the language calls for, so I've learned to accept it. I think the utility of this syntax isn't apparent unless you abandon the file-per-class pattern. You pick and choose what names to import from the module, so that no unintended namespace pollution occurs.
An alternative import syntax pulls in all names from the module, but you must qualify the names with the module when you use them. Therefore it is also name collision resistant.
import * as myModule from './myModule';
var myInstance = new myModule.MyClass();
There are exceptions to the general rule about not needing module / namespace keywords, but don't start by focusing on them. Think file == module.

Resources