ES6 Exporting and Importing a Function - javascript

If I have an ES6 JS file (test.js) ...
export default function() {
let foo = "test"
return foo
}
With Babel, is it possible to transpile and call it in an index.html file
<script src="js/text-min.js"></script>
And start using the function without needing to import it into another js file? For Ex. the next script tag after would have the following code. This is in use with Webpack --
<script>
var bar = new foo();
console.log(bar);
</script>

The transpiled version of the above code produces like this,
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function () {
var foo = "test";
return foo;
};
As you can see, it add a property to export object with the name __esModule . Nothing else. So you don't have a way to include this default function without using import or require.

What you are actually looking for is a global variable (which is probably not a great idea). For instance:
Module 1:
import {foo} from 'foo'; // still an ES6 module, just no export
// global variable 'bar'
window.bar = (...args) => foo(...args) + 6;
Then in Module 2:
bar(1, 3, 5);
Note that this defeats the entire point of using modules and should be used very sparingly.

Okay so I solved this issue by providing an output var using Webpack.
Great article that speaks to this --
http://siawyoung.com/coding/javascript/exporting-es6-modules-as-single-scripts-with-webpack.html

Related

Javascript, Difference between require and require.default [duplicate]

In Babel 5.x, I can write the following code:
app.js
export default function (){}
index.js
require('babel/register');
require('./app')();
Then, I can run node index.js with no errors. However, using Babel 6.x, running the following code
index.es6.js
require('babel-core/register');
require('./app')();
results in an error
require(...) is not a function
I want to know why?
TL;DR
You have to use
const app = require('./app').default;
app();
Explanation
Babel 5 used to have a compatibility hack for export default: if a module contained only one export, and it was a default export, it was assigned to module.exports. So, for example, your module app.js
export default function () {}
would be transpiled to this
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = function () {};
module.exports = exports["default"];
This was done purely for compatibility with require-ing Babel-transpiled modules (like you are doing). It was also inconsistent; if a module contained both named and default exports, it could not be require-d.
In reality, according to the ES6 module spec, a default export is no different than a named export with the name default. It is just syntactic sugar which can be statically resolved at compile time, so this
import something from './app';
is the same as this
import { default as something } from './app';
That being said, it appears that Babel 6 decided to drop the interoperability hack when transpiling modules. Now, your module app.js is transpiled as
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function () {};
As you see, no more assignment to module.exports. To require this module, you need to do
const app = require('./app').default;
app();
Or, more concisely, and closer to your original code:
require('./app').default();
Just to follow up with the correct answer above.
If you wanna use default export behavior of babel#5, you can try babel-plugin-add-module-exports plugin.
It's working pretty well for me.
If this doesn't work
require('./app').default()
use
require('./app').default
Without the function call at the end.

Package an ES6 class from node to browser

I have been trying to package a super simple ES6 style class object to the browser. Example
class Foo {
constructor(){
this.name = "Foo";
}
}
module.exports = Foo;
Sure I can use this in my node projects just fine, but it's also a useful object for the browser. So far I have tried webpack and browserify, but they do not expose the object globally. I was hoping to basically include the generated script file and be able to call let foo = new Foo() from the browser. How can I do this?
*note that literally the only thing stopping me from just including the file in the browser directly is the module.exports
Without hacking a huge library, or building another node module to somehow automate this, I thought of that famous razor today and just did this.
if(typeof module !== "undefined"){
module.exports = Foo;
}
For Javascript 6 you wanna do something like:
class Foo {
constructor(){
this.name = "Foo";
}
}
export default Foo;
(use export instead of module.exports)
Then from another js file do:
import Foo from '<path_to_foo>';
For example, if Foo.js is in the same dir as the "other" module, then you can do: import Foo from './Foo';
Then you can do let f = new Foo()
Modern browsers tend to support js modules, however it is not guaranteed.
Hope this helps

How do I import an IIFE-based JavaScript module into an Angular TypeScript app?

So I have a third-party SDK written as an oldschool IIFE based module. In other words it looks something like this:
var ThirdPartySDK = (function() {
var export = {};
// Add some methods to export
return export;
})();
You would then be expected to use it by referencing it on the global scope like this:
<html>
<body>
<script src="lib/ThirdPartySDK.js">
<script>
ThirdPartySDK.foo();
<\script>
<\body>
<\html>
I could still use it this way of course, but is that really the best practice with Angular and TypeScript? Is there some way to set things up with angular/TypeScript/webpack so that I can use a proper import statement? Something like this:
import { ThirdPartySDK } from '../lib/ThirdPartySDK.js';
ThirdPartySDK.foo();
The best way to have a proper import statement for the actual value of ThirdPartySDK is to refactor the script to a module that exports this value. The following snippet allows you to use the import statement as showed:
export const ThirdPartySDK = {
foo() { console.log('Doing foo'); }
};
For big libraries refactoring is not always that easy, so I see 2 approaches that do not involve too much refactoring:
1. Export the ThirdPartySDK variable
You could simply make a module out of the IIFE file by exporting the current IThirdPartySDK variable (returned by the IIFE), and then import it as you showed:
export const ThirdPartySDK = (function() {
var _export = {};
// Add some methods to export
return _export;
})();
Note that if you want to have some useful information about the shape of ThirdPartySDK you would have to add a type annotation to the const declaration, and if SomeType (see below) does not yet exist you'll have to write it yourself:
export const ThirdPartySDK: SomeType = (function() {
// ...
At this point Typescript will start to complain about the IIFE expression not being assignable to SomeType; the quick 'solution' to tell typescript to pretend the expression evaluates to a value of type SomeType using the as keyword:
export const ThirdPartySDK: SomeType = (function() {
// ...
})() as SomeType;
2. Keep the <script> tag and declare the variable
Another option it to keep the script tag, import nothing, and declare the variable and its expected type in typescript:
(But also in this case you might have to provide type definitions yourself)
interface SomeType {
// SDK type shape goes here...
}
declare const ThirdPartySDK: SomeType;
You can wrap the third-party SDK in a TypeScript module using a hack with eval.
Let's say that ThirdPartySDK.js looks like this:
var ThirdPartySDK = (function () {
var exports = {
foo: function () { console.log("Hello, world!"); }
};
return exports;
})();
You would then create a ThirdPartySDK-wrapper.ts module that looks something like this:
import * as fs from 'fs';
const script = fs.readFileSync('../lib/ThirdPartySDK.js').toString();
global.eval(script);
//#ts-ignore
export default ThirdPartySDK;
The #ts-ignore directive is required to keep the TypeScript compiler from complaining about not finding a declaration for the ThirdPartySDK variable (it is declared in the script executed through eval).
You can then import ThirdPartySDK through the wrapper module:
import ThirdPartySDK from './wrapper';
ThirdPartySDK.foo(); // Console output: "Hello, world!"
Note that this wrapper only works for applications running in Node.js, since it uses fs.fileReadSync to get the contents of the script.
If you're going to use it in a browser, you will need some other way to retrieve the script. You could probably use frameworks such as WebPack to bundle the ThirdPartySDK script as a string value that you can require in the wrapper module.

How to test globals with Mocha?

When testing with Mocha, how dose one load foo.js so that the foo-test.js can test global variables such as giveMeFive()?
foo.js:
function giveMeFive() {
return 'High five!');
}
foo-test.js:
describe('#convertNumberToDollar()', function() {
it('should get a successful high five', () => {
assert.ok( giveMeFive() === 'High five!', ' successful high five.' );
});
});
It would be ideal if the solution can be automated via Bamboo.
You can just use import * as thisNameDoesntMatter from 'foo' or, using es5 style (which is cleaner in this case), require('foo') and globals defined in that file will be available on the global scope.
For instance, if you define the following JavaScript file:
//test.js
aGlobalVariable = 'someValue';
let aLocalVariable = 'anotherValue';
export default aLocalVariable;
This is the code Babel will generate (using the es2015 preset and the "node" environment):
//test-compiled.js
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
global.aGlobalVariable = 'someValue';
var aLocalVariable = 'anotherValue';
exports.default = aLocalVariable;
As you can see, the aGlobalVariable declaration is kept in the module. As the entire compiled file will be evaluated when it's imported, aGlobalVariable will be available on the global scope whenever you import this module (if you chose the "browser" environment, it would be defined on window). You don't need to explicitly request the globals to be imported.

How do you share constants in NodeJS modules?

Currently I'm doing this:
foo.js
const FOO = 5;
module.exports = {
FOO: FOO
};
And using it in bar.js:
var foo = require('foo');
foo.FOO; // 5
Is there a better way to do this? It feels awkward to declare the constant in the exports object.
In my opinion, utilizing Object.freeze allows for a DRYer and more declarative style. My preferred pattern is:
./lib/constants.js
module.exports = Object.freeze({
MY_CONSTANT: 'some value',
ANOTHER_CONSTANT: 'another value'
});
./lib/some-module.js
var constants = require('./constants');
console.log(constants.MY_CONSTANT); // 'some value'
constants.MY_CONSTANT = 'some other value';
console.log(constants.MY_CONSTANT); // 'some value'
Technically, const is not part of the ECMAScript specification. Also, using the "CommonJS Module" pattern you've noted, you can change the value of that "constant" since it's now just an object property. (not sure if that'll cascade any changes to other scripts that require the same module, but it's possible)
To get a real constant that you can also share, check out Object.create, Object.defineProperty, and Object.defineProperties. If you set writable: false, then the value in your "constant" cannot be modified. :)
It's a little verbose, (but even that can be changed with a little JS) but you should only need to do it once for your module of constants. Using these methods, any attribute that you leave out defaults to false. (as opposed to defining properties via assignment, which defaults all the attributes to true)
So, hypothetically, you could just set value and enumerable, leaving out writable and configurable since they'll default to false, I've just included them for clarity.
Update - I've create a new module (node-constants) with helper functions for this very use-case.
constants.js -- Good
Object.defineProperty(exports, "PI", {
value: 3.14,
enumerable: true,
writable: false,
configurable: false
});
constants.js -- Better
function define(name, value) {
Object.defineProperty(exports, name, {
value: value,
enumerable: true
});
}
define("PI", 3.14);
script.js
var constants = require("./constants");
console.log(constants.PI); // 3.14
constants.PI = 5;
console.log(constants.PI); // still 3.14
ES6 way.
export in foo.js
const FOO = 'bar';
module.exports = {
FOO
}
import in bar.js
const {FOO} = require('foo');
You can explicitly export it to the global scope with global.FOO = 5. Then you simply need to require the file, and not even save your return value.
But really, you shouldn't do that. Keeping things properly encapsulated is a good thing. You have the right idea already, so keep doing what you're doing.
From previous project experience, this is a good way:
In the constants.js:
// constants.js
'use strict';
let constants = {
key1: "value1",
key2: "value2",
key3: {
subkey1: "subvalue1",
subkey2: "subvalue2"
}
};
module.exports =
Object.freeze(constants); // freeze prevents changes by users
In main.js (or app.js, etc.), use it as below:
// main.js
let constants = require('./constants');
console.log(constants.key1);
console.dir(constants.key3);
import and export (prob need something like babel as of 2018 to use import)
types.js
export const BLUE = 'BLUE'
export const RED = 'RED'
myApp.js
import * as types from './types.js'
const MyApp = () => {
let colour = types.RED
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
I found the solution Dominic suggested to be the best one, but it still misses one feature of the "const" declaration. When you declare a constant in JS with the "const" keyword, the existence of the constant is checked at parse time, not at runtime. So if you misspelled the name of the constant somewhere later in your code, you'll get an error when you try to start your node.js program. Which is a far more better misspelling check.
If you define the constant with the define() function like Dominic suggested, you won't get an error if you misspelled the constant, and the value of the misspelled constant will be undefined (which can lead to debugging headaches).
But I guess this is the best we can get.
Additionally, here's a kind of improvement of Dominic's function, in constans.js:
global.define = function ( name, value, exportsObject )
{
if ( !exportsObject )
{
if ( exports.exportsObject )
exportsObject = exports.exportsObject;
else
exportsObject = exports;
}
Object.defineProperty( exportsObject, name, {
'value': value,
'enumerable': true,
'writable': false,
});
}
exports.exportObject = null;
In this way you can use the define() function in other modules, and it allows you to define constants both inside the constants.js module and constants inside your module from which you called the function. Declaring module constants can then be done in two ways (in script.js).
First:
require( './constants.js' );
define( 'SOME_LOCAL_CONSTANT', "const value 1", this ); // constant in script.js
define( 'SOME_OTHER_LOCAL_CONSTANT', "const value 2", this ); // constant in script.js
define( 'CONSTANT_IN_CONSTANTS_MODULE', "const value x" ); // this is a constant in constants.js module
Second:
constants = require( './constants.js' );
// More convenient for setting a lot of constants inside the module
constants.exportsObject = this;
define( 'SOME_CONSTANT', "const value 1" ); // constant in script.js
define( 'SOME_OTHER_CONSTANT', "const value 2" ); // constant in script.js
Also, if you want the define() function to be called only from the constants module (not to bloat the global object), you define it like this in constants.js:
exports.define = function ( name, value, exportsObject )
and use it like this in script.js:
constants.define( 'SOME_CONSTANT', "const value 1" );
I think that const solves the problem for most people looking for this anwwer. If you really need an immutable constant, look into the other answers.
To keep everything organized I save all constants on a folder and then require the whole folder.
src/main.js file
const constants = require("./consts_folder");
src/consts_folder/index.js
const deal = require("./deal.js")
const note = require("./note.js")
module.exports = {
deal,
note
}
Ps. here the deal and note will be first level on the main.js
src/consts_folder/note.js
exports.obj = {
type: "object",
description: "I'm a note object"
}
Ps. obj will be second level on the main.js
src/consts_folder/deal.js
exports.str = "I'm a deal string"
Ps. str will be second level on the main.js
Final result on main.js file:
console.log(constants.deal);
Ouput:
{ deal: { str: 'I\'m a deal string' },
console.log(constants.note);
Ouput:
note: { obj: { type: 'object', description: 'I\'m a note object' } }
As an alternative, you can group your "constant" values in a local object, and export a function that returns a shallow clone of this object.
var constants = { FOO: "foo" }
module.exports = function() {
return Object.assign({}, constants)
}
Then it doesn't matter if someone re-assigns FOO because it will only affect their local copy.
Since Node.js is using the CommonJS patterns, you can only share variables between modules with module.exports or by setting a global var like you would in the browser, but instead of using window you use global.your_var = value;.
I ended up doing this by exporting a frozen object with anonymous getter functions, rather than the constants themselves. This reduces the risk of nasty bugs introduced due to a simple typo of the const name, as a runtime error will be thrown in case of a typo. Here's a full example that also uses ES6 Symbols for the constants, ensuring uniqueness, and ES6 arrow functions. Would appreciate feedback if anything in this approach seems problematic.
'use strict';
const DIRECTORY = Symbol('the directory of all sheets');
const SHEET = Symbol('an individual sheet');
const COMPOSER = Symbol('the sheet composer');
module.exports = Object.freeze({
getDirectory: () => DIRECTORY,
getSheet: () => SHEET,
getComposer: () => COMPOSER
});
I recommend doing it with webpack (assumes you're using webpack).
Defining constants is as simple as setting the webpack config file:
var webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'APP_ENV': '"dev"',
'process.env': {
'NODE_ENV': '"development"'
}
})
],
};
This way you define them outside your source, and they will be available in all your files.
I don't think is a good practice to invade the GLOBAL space from modules, but in scenarios where could be strictly necessary to implement it:
Object.defineProperty(global,'MYCONSTANT',{value:'foo',writable:false,configurable:false});
It has to be considered the impact of this resource. Without proper naming of those constants, the risk of OVERWRITTING already defined global variables, is something real.

Categories