Pass options to ES6 module imports
Solution 1
There is no way to do this with a single import
statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import
operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Solution 2
Concept
Here's my solution using ES6
Very much inline with @Bergi's response, this is the "template" I use when creating imports that need parameters passed for class
declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel
with Webpack
):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo
in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical
in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Solution 3
Building on @Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
Solution 4
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2)
or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... })
and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js
has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
Solution 5
I believe you can use es6 module loaders. http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
Related videos on Youtube
Fabrizio Giordano
Updated on July 11, 2022Comments
-
Fabrizio Giordano almost 2 years
Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
-
adeneo about 9 yearsNot sure you can, there's a module loader API, or at least there was at some time, that used something like
System.import(module)
, not sure if that allows arguments or not, someone who knows more about ES6 probably does ? -
Matt Browne almost 7 yearsThere is a proposed solution for this, for which there are already implementations in node.js (via a plugin) and webpack: 2ality.com/2017/01/import-operator.html
-
-
Fabrizio Giordano about 9 yearsThank you I wish there was something like
import x from 'module' use someoptions;
kinda of syntax -
Felix Kling about 9 years@Fabrizio: If you think about it further, it wouldn't be that helpful really. It would only work if the module exports a function and should probably not be allowed if we have named imports (i.e.
import {x, y} from 'module'
). Then what should the syntax be if I want to pass multiple arguments? Or spread an array of arguments? It's a narrow use case and basically you are trying to add a different syntax for a function call, but we already have function calls which allow us to deal with all the other cases. -
Fabrizio Giordano about 9 years@FelixKling I completely agree with you. I was converting an old express webapp and I encounter
var session = require('express-session'); var RedisStore = require('connect-redis')(session);
I was just wondering if there was a one line solution. I can totally survive with split the RedisStore assignment in 2 lines :) -
Bergi about 9 years@FabrizioGiordano: I could imagine something like
import {default(someoptions) as x} from 'module'
in ES7, if there is really a need for this. -
Jeff Handley almost 9 yearsFor the
session
/connect-redis
example, I have been imagining syntax like this:import session from 'express-session'); import RedisStore(session) from 'connect-redis'
. -
Stijn de Witt about 8 yearsBut where does the result of
m(youroptionshere)
end up? I suppose you could writeSystem.import('lib/math').then(m => m(options)).then(module => { /* code using module here */})
... but it's not very clear. -
Robert Moskal almost 8 yearsWow I can't believe there's no elegant way to do this in E6. That's the way I mainly write modules.
-
RavenHursT over 7 yearsI can def see a need for this, and I'm def trying to solve it right now. I have a module that needs to be a true singleton across my app. The problem is, one of my other modules needs to access it in it's runtime so by the time my index.js runs, and I'm able to configure the singleton, the other module's code has already been hoisted and ran. This is really frustrating. Wrapping the export works only if you're ok w/ a new instance every time. But if you want the same object to be config'd and shared, you're SOL w/
import
:-( -
Admin over 7 yearsSince all your imported modules are classes, why not pass the parameter when instantiating the class?
-
Swivel over 7 years@jasonszhao The most important thing to note here is that the class
MyView
extends certain items available in the framework's namespace. While it's absolutely possible to simply pass it in as a parameter to the class, it also depends on when and where the class is instantiated; portability is then affected. In practice, these classes may be handed over to other frameworks that may instantiate them differently (e.g., custom React components). When the class finds itself outside framework scope, it can still maintain access to the framework when instantiated because of this methodology. -
Matt Browne almost 7 yearsIt looks like official support for dynamic imports will be coming aoon, and implementations already exist for node and webpack: 2ality.com/2017/01/import-operator.html
-
Bergi almost 7 years@MattBrowne Thanks for the info, but that doesn't really change the picture for the static case. You can't do the
const x = (await import(…))(options)
in a module scope. (Yet?) -
Dan Dascalescu over 5 yearsWhy post an answer that duplicates an already posted one?
-
Akinwale Folorunsho Habib about 5 yearsMy bad. Never saw the mentioned post. Just looked at the question and took a stab at it. Thanks for bringing it to my notice.
-
TSR about 5 years@Bergi How do you export multiple module, not just one with the same parameter?
-
TSR about 5 years
export default function (GreetingIntroTxt:string) { class Student { name: string; constructor(name: string) { this.name = name; } greet() { return
"${GreetingIntroTxt}, " + this.greeting; } } return { Student,} }
This returns a error: TS4060: Return type of exported function has or is using private name 'class' -
TSR about 5 years@Swivel Please assist I need help with similar issue: stackoverflow.com/questions/55214957/…
-
Dan Dascalescu over 4 yearsThat's simply passing parameters to a function you've imported and are calling. It's not passing any options to the module you import it from.
xModule
is misleading here. What you actually have isimport func from 'module'; func('someOptions');
. -
Dan Dascalescu over 4 yearsYou're welcome. You can also delete the duplicate answer if you'd like.
-
TamusJRoyce almost 3 yearstypescript with "module": "commonjs" and "esModuleInterop": true in tsconfig.js -
import * as createPrompt from '../node_modules/prompt-sync'; const prompt = (createPrompt.default ?? createPrompt)();
so this works with both tsc and ts-node commands