Javascript import all modules from a file as a global variables - javascript

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!

Related

Using 'export' keyword solely for importing into Unit Tests

I'm using Meteor and am writing unit tests for a Collection. I've got Helper methods for the collection in addition to just regular JS functions.
I.e.
Collection.helpers({
helperFn: function () {
return 'foo';
}
});
//And in the same file
function bar() {
return "bar";
}
Then in my tests file I have something like
import { Collection } from '../collections'
//Use Factory or Stub to create test Document
//This then works just fine and I can assert, etc..
testDoc.helperFn
My question is with wanting to test just the regular 'bar' JS function. This isn't a big deal using ES6 classes because then I can just export the whole class and call any function with an instance of it. But with Meteor I'm finding the only way I can access the function is by using the 'export' keyword.
So in my Collection file
export function bar ({ return bar; });
And now in my test file I'd do something like
import { bar } from '../collection'
I'd rather not add an export statement for every time I test a new function. Is there any way around this or is it not a big deal?
I do think that the export/import is the way to go, but to answer the first part of your question: yes, you can fall back to the original scoping of meteor and put these functions in the global scope of meteor as follows:
do not put your files in the imports/ folder, but into another folder in your project, e.g., server/.
defined the functions as:
bar = function() { /* function body */ }
These variables are interpreted by meteor as being global to the project, and hence do not need to be imported before use.
That said, there was a reason meteor introduced the imports/ folder and corresponding export/import paradigm in version 1.3. It avoids polluting the global scope and makes it much easier to see where things are defined.

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']();

Are export const statements visible inside the file they reside?

Are export const statements visible inside the file they reside?
export const MAX_NAME_LENGTH = 20;
console.log(MAX_NAME_LENGTH); // can I do this?
Yes, they are. The declaration is still a declaration in the scope in which it appears (e.g., the module's scope, since export can only appear there) and all the usual rules about the declaration apply; all export does is export it.
In that specific case, since it's a const declaration, the "usual rules" include the temporal dead zone above the declaration in which MAX_NAME_LENGTH cannot be used. If you'd exported a function declaration like this:
export function foo() {
}
...the "usual rules" mean you could use foo anywhere in your module code, because it's still hoisted. (Same for export var x;.)
(You can also test things like this with an up-to-date version of Chrome, which now supports modules. :-) )

Importing a self invoking function

I have created a particular effect and wrapped it into a self-invoking function in myEffect.js file,
(function () {
// yada yada...
}());
Is it possible to then use the es6 way of importing to import this into my main file so that it will run as it is? Reason I'm doing this is my main js file has other misc stuff and this effect is pretty long in itself and I'm hoping to be able to split things up.
The effect will run when the module is evaluated, which happens when it is imported at least once in some other module.
You don't need an IIFE at all, ES6 modules already provide their own scope.
You don't need to export anything, as all your module is supposed to do is execute the side effect. It does not have a result value. (Which could be considered a design flaw, but let's not discuss that).
All you need to do is
// myEffect.js
… // yada yada
// main.js
import 'myEffect.js';
Yes you can import the file as such and execute the function wherever you want
for example
module.exports=(function () {
// yada yada...
}());
var effect = require('./effect.js)
now using effect you can access those in the file.

Convert closure to es6 module

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

Categories

Resources