Pass options to ES6 module imports

67,417

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);
});
Share:
67,417

Related videos on Youtube

Fabrizio Giordano
Author by

Fabrizio Giordano

Updated on July 11, 2022

Comments

  • Fabrizio Giordano
    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
      adeneo about 9 years
      Not 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
      Matt Browne almost 7 years
      There 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
    Fabrizio Giordano about 9 years
    Thank you I wish there was something like import x from 'module' use someoptions; kinda of syntax
  • Felix Kling
    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
    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
    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
    Jeff Handley almost 9 years
    For 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
    Stijn de Witt about 8 years
    But where does the result of m(youroptionshere) end up? I suppose you could write System.import('lib/math').then(m => m(options)).then(module => { /* code using module here */})... but it's not very clear.
  • Robert Moskal
    Robert Moskal almost 8 years
    Wow I can't believe there's no elegant way to do this in E6. That's the way I mainly write modules.
  • RavenHursT
    RavenHursT over 7 years
    I 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
    Admin over 7 years
    Since all your imported modules are classes, why not pass the parameter when instantiating the class?
  • Swivel
    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
    Matt Browne almost 7 years
    It 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
    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
    Dan Dascalescu over 5 years
    Why post an answer that duplicates an already posted one?
  • Akinwale Folorunsho Habib
    Akinwale Folorunsho Habib about 5 years
    My 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
    TSR about 5 years
    @Bergi How do you export multiple module, not just one with the same parameter?
  • TSR
    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
    TSR about 5 years
    @Swivel Please assist I need help with similar issue: stackoverflow.com/questions/55214957/…
  • Dan Dascalescu
    Dan Dascalescu over 4 years
    That'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 is import func from 'module'; func('someOptions');.
  • Dan Dascalescu
    Dan Dascalescu over 4 years
    You're welcome. You can also delete the duplicate answer if you'd like.
  • TamusJRoyce
    TamusJRoyce almost 3 years
    typescript 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