Convert closure to es6 module - javascript

I'm using a javascript build environment that supports es6 modules (using es6-module-transpiler) so you can simply import stuff across different files.
Now I got a third party library that I'd like to be "importable".
The library populates its functionality like this:
(function () {/*...*/}).call(this);
Would it be safe to omit the closure and convert it to:
export default function () {/* ... */};
Or is there a better way?
Thanks in advance!

The original code you show invokes the anonymous function, which to make any sense must define a global variable, whereas the second code fragment you show merely exports the function, which is a different thing.
For purposes of discussion, let's assume the original code defines a global like this:
// my-third-party-module.js
(function() {
let myVar = 22;
window.MyThirdPartyModule = { log: function() { console.log(myVar); } };
}.call(this);
and you are using is as so:
// app.js
MyThirdPartyModule.log();
You could rewrite this as
// my-third-party-module.js
let myVar = 22;
export default { log: function() { console.log(myVar); } };
// app.js
import MyThirdPartyModule from `my-third-party-module';
MyThirdPartyModule.log();
Note that we have moved the variable myVar which was local to the anonymous function to the top module level.
However, depending on your preferences, rather than exporting a big object, which is sort of a pre-module mentality, you might want to export its APIs individually:
// my-third-party-module.js
let myVar = 22;
export function log { console.log(myVar); }
// app.js
import {log} from `my-third-party-module';
log();
or if you prefer
// app.js
import * as MyThirdPartyModule from `my-third-party-module';
MyThirdPartyModule.log();
However, all of these approaches assume you are able and willing to edit the source of the third party library. If that is not the case, you could write a little piece of glue code, such as
// my-third-party-module-interface.js
import 'my-third-party-module'; // This will run the module.
export default MyThirdPartyModule; // Export the global it defined.
// app.js
import MyThirdPartyModule from 'my-third-party-module-interface';
If you would prefer again to export individual APIs, you could extend the glue to re-export each of them:
// my-third-party-module-interface.js
import 'my-third-party-module'; // This will run the module.
const {log, otherAPI, ...} = MyThirdPartyModule;
export {log, otherAPI, ...};
// app.js
import {log} from 'my-third-party-module-interface';

The conversion of legacy dependencies is still an issue. And the horrible workflow they use makes things a lot harder, prefixing the actual code with browserify and webpack silliness.
So what to do? Existentially, the library is guaranteed only to deposit a global in window but by obscure and weird ways. And all slightly different.
So let the legacy simply do what it is supposed to do for you, but wrapped in a module so that you can import it rather than use a script tag:
https://medium.com/#backspaces/es6-modules-part-2-libs-wrap-em-up-8715e116d690

Related

js import via building the string [duplicate]

Conditional
Is it possible to have conditional import statements like below?
if (foo === bar) {
import Baz from './Baz';
}
I have tried the above but get the following error (from Babel) when compiling.
'import' and 'export' may only appear at the top level
Dynamic
Is it possible to have dynamic import statements like below?
for (let foo in bar) {
if (bar.hasOwnProperty(foo)) {
import Baz from `./${foo}`;
}
}
The above receives the same error from Babel whilst compiling.
Is this possible to do or is there something I am missing?
Reasoning
The reason I am trying to do this is that I have a lot of imports for a number of "pages" and they follow a similar pattern. I would like to clean up my code base by importing these files with a dynamic for loop.
If this is not possible then is there a better way to handle large number of imports in ES6?
We do have dynamic imports proposal now with ECMA. This is in stage 2. This is also available as babel-preset.
Following is way to do conditional rendering as per your case.
if (foo === bar) {
import('./Baz')
.then((Baz) => {
console.log(Baz.Baz);
});
}
This basically returns a promise. Resolution of promise is expected to have the module. The proposal also has things like multiple dynamic imports, default imports, js file import etc. You can find more information about dynamic imports here.
You can't resolve dynamically your dependencies, as imports are meant for static analysis. However, you can probably use some require here, something like:
for (let foo in bar) {
if (bar.hasOwnProperty(foo)) {
const Baz = require(foo).Baz;
}
}
As this question is highly-ranked by Google, it is worth pointing out that things have changed since the older answers were posted.
MDN has this entry under 'Dynamic Imports':
The import keyword may be called as a function to dynamically import a
module. When used this way, it returns a promise.
import('/modules/my-module.js')
.then((module) => {
// Do something with the module.
});
This form also supports the await keyword.
let module = await import('/modules/my-module.js');
MDN also has a more detailed explanation.
A useful article on the subject can be found on Medium.
Since 2016 a lot has passed in JavaScript world, so I believe it's time to offer most updated info on this topic. Currently Dynamic imports are a reality both on Node and on browsers (natively if you don't care about IE, or with #babel/plugin-syntax-dynamic-import if you do care).
So, consider a sample module something.js with two named exports and one default export:
export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')
We can use import() syntax to easily and cleanly load it conditionally:
if (somethingIsTrue) {
import('./something.js').then((module) => {
// Use the module the way you want, as:
module.hi('Erick') // Named export
module.bye('Erick') // Named export
module.default() // Default export
})
}
But since the return is a Promise, the async/await syntactic sugar is also possible:
async imAsyncFunction () {
if (somethingIsTrue) {
const module = await import('./something.js')
module.hi('Erick')
}
}
Now think about the possibilities along with Object Destructuring Assignment! For example, we are able to easily put only one of those named exports in memory for posterior use:
const { bye } = await import('./something.js')
bye('Erick')
Or maybe grab one of those named exports and rename it to anything else we want:
const { hi: hello } = await import('./something.js')
hello('Erick')
Or even rename the default exported function to something that makes more sense:
const { default: helloWorld } = await import('./something.js')
helloWorld()
Just a last (but no least) note: import() may looks like a function call, but it isn't a Function. It's a special syntax that just happens to use parentheses (similar to what happens with super()). So it's not possible to assign import to a variable or use things of the Function prototype, like call/apply.
Require will not solve your problem as it is a synchronous call. There are several options and they all involve
Asking for the module you need
Waiting for a promise to return the module
In ECMA Script there is support for lazy loading modules using SystemJS. This of course isn't supported in all browsers, so in the meantime you can use JSPM or a SystemJS shim.
https://github.com/ModuleLoader/es6-module-loader

Javascript import all modules from a file as a global variables

In script1.js, I have the following exports:
export function test1() {
// do something
}
export function test2() {
// do something
}
I am importing script1.js into script2.js:
import * as functions from './script1.js';
functions.test1();
functions.test2();
I am wondering if there is a way to import all the modules from script1.js, and directly place them inside the global variables of script2.js. So I can directly access them with test1(), test2(), without the intermediate step of putting them in an object.
I know that we can do import {test1, test2} from './script1.js';, but if I have a lot of exports, then the deconstruction will be tedious.
In another word, what I am looking for is something equivalent of:
import * from './script1.js';
test1();
test2();
The above code block is hypothetical, and it does not work.
Given your current code, it's not possible. The whole point of modules is to avoid implicit global pollution. To do something like this, you'd have to explicitly assign the functions to the global object inside the module, eg, change:
export function test1() {
// do something
}
to
window.test1 = function test1() {
// do something
};
and then, when the module is imported, the functions will be available globally:
import './script1.js';
But this is an antipattern:
but if I have a lot of exports, then the desconstruction will be tedious.
It does require some boilerplate, but don't be afraid of it - in larger projects, having explicit dependency chains is a huge plus for maintainability. It also permits tree-shaking, which isn't possible for modules with side-effects only (like assigning to the global object).
you can also very simply auto generate the import , with a script like this:
import * as functions from './script1.js';
const function_names = Object.keys(functions).join(',')
console.log( function_names ) //output: test1,test2
your list is ready in your console!

Calling an exported function by a string value of the function name

This is a very simple example of what I am trying to achieve, basically I want to call a function by a string value of it's name, e.g. "hello" should call hello()
I have a helper.js file which contains an exported function e.g.
export function hello() {
console.log('is it me you`re looking for?');
}
I am importing this into another js file for usage
import {hello} from './helper';
I have tried using eval, window and new Function to call my function but no luck
//getting console error "hello is not defined"
eval('hello()');
var fn = window['hello()'];
fn();
var fn = new Function('hello()');
fn();
If I wrap the function like so, the eval fires the wrapper.
function helloWrapper() {
hello();
}
eval('helloWrapper()');
I just cant seem to fire the exported hello() function directly. I have around 10 functions I'll need to fire so having a wrapper for each seems a bit hacky and wondering if there is a way I can achieve this?
Any help would be greatly appreciated if anyone can point me in the right direction.
Thanks in advance
eval("hello()") should work just fine -- but it's not how you should do this. :-)
Instead, build an object containing the functions:
import {hello} from './helper'; // In some environments, these need the
import {groot} from './groot'; // .js on the filenames.
// ...
const functions = {hello, groot/*, ... */};
and then call them like this:
functions[name]();
Live example on plnkr.co
Generally, referring functions by their names is unsafe in client-side code - or any other that can be minified. The approach explained in the question will work only because hello isn't just function name but an import. Due to how ES modules work, import names will be preserved on minification.
In order for a function to be referred by its name, it should be object property. In case of imports there's already such object, it's module export:
import * as helper from './helper';
helper['hello']();
In case there are multiple modules where functions may originate from, there should be intermediate module that re-exports them.
export * from './helper';
export * from './another-helper';
All functions from underlying modules will be available as properties when it's imported as *:
import * as helper from './reexported-helpers';
helper['hello']();
helper['bye']();

ES6 import in iteration & not at top-level [duplicate]

Conditional
Is it possible to have conditional import statements like below?
if (foo === bar) {
import Baz from './Baz';
}
I have tried the above but get the following error (from Babel) when compiling.
'import' and 'export' may only appear at the top level
Dynamic
Is it possible to have dynamic import statements like below?
for (let foo in bar) {
if (bar.hasOwnProperty(foo)) {
import Baz from `./${foo}`;
}
}
The above receives the same error from Babel whilst compiling.
Is this possible to do or is there something I am missing?
Reasoning
The reason I am trying to do this is that I have a lot of imports for a number of "pages" and they follow a similar pattern. I would like to clean up my code base by importing these files with a dynamic for loop.
If this is not possible then is there a better way to handle large number of imports in ES6?
We do have dynamic imports proposal now with ECMA. This is in stage 2. This is also available as babel-preset.
Following is way to do conditional rendering as per your case.
if (foo === bar) {
import('./Baz')
.then((Baz) => {
console.log(Baz.Baz);
});
}
This basically returns a promise. Resolution of promise is expected to have the module. The proposal also has things like multiple dynamic imports, default imports, js file import etc. You can find more information about dynamic imports here.
You can't resolve dynamically your dependencies, as imports are meant for static analysis. However, you can probably use some require here, something like:
for (let foo in bar) {
if (bar.hasOwnProperty(foo)) {
const Baz = require(foo).Baz;
}
}
As this question is highly-ranked by Google, it is worth pointing out that things have changed since the older answers were posted.
MDN has this entry under 'Dynamic Imports':
The import keyword may be called as a function to dynamically import a
module. When used this way, it returns a promise.
import('/modules/my-module.js')
.then((module) => {
// Do something with the module.
});
This form also supports the await keyword.
let module = await import('/modules/my-module.js');
MDN also has a more detailed explanation.
A useful article on the subject can be found on Medium.
Since 2016 a lot has passed in JavaScript world, so I believe it's time to offer most updated info on this topic. Currently Dynamic imports are a reality both on Node and on browsers (natively if you don't care about IE, or with #babel/plugin-syntax-dynamic-import if you do care).
So, consider a sample module something.js with two named exports and one default export:
export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')
We can use import() syntax to easily and cleanly load it conditionally:
if (somethingIsTrue) {
import('./something.js').then((module) => {
// Use the module the way you want, as:
module.hi('Erick') // Named export
module.bye('Erick') // Named export
module.default() // Default export
})
}
But since the return is a Promise, the async/await syntactic sugar is also possible:
async imAsyncFunction () {
if (somethingIsTrue) {
const module = await import('./something.js')
module.hi('Erick')
}
}
Now think about the possibilities along with Object Destructuring Assignment! For example, we are able to easily put only one of those named exports in memory for posterior use:
const { bye } = await import('./something.js')
bye('Erick')
Or maybe grab one of those named exports and rename it to anything else we want:
const { hi: hello } = await import('./something.js')
hello('Erick')
Or even rename the default exported function to something that makes more sense:
const { default: helloWorld } = await import('./something.js')
helloWorld()
Just a last (but no least) note: import() may looks like a function call, but it isn't a Function. It's a special syntax that just happens to use parentheses (similar to what happens with super()). So it's not possible to assign import to a variable or use things of the Function prototype, like call/apply.
Require will not solve your problem as it is a synchronous call. There are several options and they all involve
Asking for the module you need
Waiting for a promise to return the module
In ECMA Script there is support for lazy loading modules using SystemJS. This of course isn't supported in all browsers, so in the meantime you can use JSPM or a SystemJS shim.
https://github.com/ModuleLoader/es6-module-loader

ES6: Conditional & Dynamic Import Statements

Conditional
Is it possible to have conditional import statements like below?
if (foo === bar) {
import Baz from './Baz';
}
I have tried the above but get the following error (from Babel) when compiling.
'import' and 'export' may only appear at the top level
Dynamic
Is it possible to have dynamic import statements like below?
for (let foo in bar) {
if (bar.hasOwnProperty(foo)) {
import Baz from `./${foo}`;
}
}
The above receives the same error from Babel whilst compiling.
Is this possible to do or is there something I am missing?
Reasoning
The reason I am trying to do this is that I have a lot of imports for a number of "pages" and they follow a similar pattern. I would like to clean up my code base by importing these files with a dynamic for loop.
If this is not possible then is there a better way to handle large number of imports in ES6?
We do have dynamic imports proposal now with ECMA. This is in stage 2. This is also available as babel-preset.
Following is way to do conditional rendering as per your case.
if (foo === bar) {
import('./Baz')
.then((Baz) => {
console.log(Baz.Baz);
});
}
This basically returns a promise. Resolution of promise is expected to have the module. The proposal also has things like multiple dynamic imports, default imports, js file import etc. You can find more information about dynamic imports here.
You can't resolve dynamically your dependencies, as imports are meant for static analysis. However, you can probably use some require here, something like:
for (let foo in bar) {
if (bar.hasOwnProperty(foo)) {
const Baz = require(foo).Baz;
}
}
As this question is highly-ranked by Google, it is worth pointing out that things have changed since the older answers were posted.
MDN has this entry under 'Dynamic Imports':
The import keyword may be called as a function to dynamically import a
module. When used this way, it returns a promise.
import('/modules/my-module.js')
.then((module) => {
// Do something with the module.
});
This form also supports the await keyword.
let module = await import('/modules/my-module.js');
MDN also has a more detailed explanation.
A useful article on the subject can be found on Medium.
Since 2016 a lot has passed in JavaScript world, so I believe it's time to offer most updated info on this topic. Currently Dynamic imports are a reality both on Node and on browsers (natively if you don't care about IE, or with #babel/plugin-syntax-dynamic-import if you do care).
So, consider a sample module something.js with two named exports and one default export:
export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')
We can use import() syntax to easily and cleanly load it conditionally:
if (somethingIsTrue) {
import('./something.js').then((module) => {
// Use the module the way you want, as:
module.hi('Erick') // Named export
module.bye('Erick') // Named export
module.default() // Default export
})
}
But since the return is a Promise, the async/await syntactic sugar is also possible:
async imAsyncFunction () {
if (somethingIsTrue) {
const module = await import('./something.js')
module.hi('Erick')
}
}
Now think about the possibilities along with Object Destructuring Assignment! For example, we are able to easily put only one of those named exports in memory for posterior use:
const { bye } = await import('./something.js')
bye('Erick')
Or maybe grab one of those named exports and rename it to anything else we want:
const { hi: hello } = await import('./something.js')
hello('Erick')
Or even rename the default exported function to something that makes more sense:
const { default: helloWorld } = await import('./something.js')
helloWorld()
Just a last (but no least) note: import() may looks like a function call, but it isn't a Function. It's a special syntax that just happens to use parentheses (similar to what happens with super()). So it's not possible to assign import to a variable or use things of the Function prototype, like call/apply.
Require will not solve your problem as it is a synchronous call. There are several options and they all involve
Asking for the module you need
Waiting for a promise to return the module
In ECMA Script there is support for lazy loading modules using SystemJS. This of course isn't supported in all browsers, so in the meantime you can use JSPM or a SystemJS shim.
https://github.com/ModuleLoader/es6-module-loader

Categories

Resources