I've written an ES6 module that looks something like this:
export default function({makeCurrentVerUrl, verUrl, fileServer, downloadTokenType, appId}) {
...
}
When compiled by webpack, it looks something like this:
webpackJsonp([5,7],[
/* 0 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (_ref) {
var makeCurrentVerUrl = _ref.makeCurrentVerUrl;
var verUrl = _ref.verUrl;
var fileServer = _ref.fileServer;
var downloadTokenType = _ref.downloadTokenType;
var appId = _ref.appId;
...
};
/***/ }
]);
Which is great, but I'm not sure how to run this file and call my default function.
I can include it,
<script src="/path/to/script.js"></script>
Which I believe will run it automatically, but how can I call the functions I've defined in it from the browser? require is not defined in my browser.
You can set output.library in the configuration. From the docs:
output.library
If set, export the bundle as library. output.library is
the name.
Use this, if you are writing a library and want to publish it as
single file.
output.libraryTarget
Which format to export the library:
"var" - Export by setting a variable: var Library = xxx (default)
"this" - Export by setting a property of this: this["Library"] = xxx
"commonjs" - Export by setting a property of exports:
exports["Library"] = xxx
"commonjs2" - Export by setting module.exports: module.exports = xxx
"amd" - Export to AMD (optionally named)
"umd" - Export to AMD, CommonJS2 or as property in root
Default: "var"
Then you will be able to do
myLibrary()
So the easiest way to do this without changing output.library (assuming you don't want to globalize everything) is to just attach your needed functions to the window. e.g.
// entry-point.js
import foo from './scripts/foo.js';
window.foo = foo;
Or if you want to attach a whole bunch of stuff to the window (not just default), you can do something like:
Object.assign(window, require('./scripts/somelib.js'));
You can also take a look at bundle-loader, the built-in webpack feature require.context or dynamic import()s if you want to include some scripts at run-time.
Related
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.
How could I declare a third party module which looks like this:
in third party module:
module.exports = function foo(){
// do somthing
}
in my code:
import * as foo from 'foo-module'; // Can not find a declaration module for ...
foo();
Check out the documentation on working with 3rd party modules.
How to write the declaration depends a lot on how the module was written and what it exports.
The example you've given is a CommonJS module (module.exports = ...) which is not really a valid ES6 module, because ES6 cannot export a function as the module (it can only export function members or a default function).
Update for TypeScript 2.7+
With the added esModuleInterop compiler option you no longer need to use the "namespace hack" shown below for CommonJS modules that have a non-ES6 compatible export.
First, make sure you've enabled esModuleInterop in your tsconfig.json (which is now included by default with tsc --init):
{
"compilerOptions" {
...
"esModuleInterop": true,
...
}
}
Declare your foo-example in a .d.ts file like this:
declare module "foo-module" {
function foo(): void;
export = foo;
}
Now you can import it as a namespace like you wanted:
import * as foo from "foo-module";
foo();
Or as a default import:
import foo from "foo-module";
foo();
Older workaround
You can declare your foo-example in a .d.ts file like this:
declare module "foo-module" {
function foo(): void;
namespace foo { } // This is a hack to allow ES6 wildcard imports
export = foo;
}
And import like you wanted:
import * as foo from "foo-module";
foo();
Or like this:
import foo = require("foo-module");
foo();
The documentation has a good resource on declaration files and some templates for various kinds of declaration files.
I had a similar problem. And struggled to add a type definition to my project. Finally, I was able to achieve it using the following steps.
This is some module (just with constants), lets call it some-module - node_modules/some-module/index.js.
'use strict';
exports.__esModule = true;
var APPS = exports.APPS = {
ona: 'ona',
tacq: 'tacq',
inetAcq: 'inetAcq'
};
First I add to tsconfig.json baseUrl and typeRoots
{
...
"compilerOptions": {
...
"baseUrl": "types",
"typeRoots": ["types"]
}
...
}
Second in my project root I create folder types with same folders structure for the module types/some-module/index.js and place the code:
declare module 'some-module' {
type Apps = {
ona: string;
tacq: string;
inetAcq: string;
};
let APPS: Apps
}
Finally I can import it in my my-file.ts with typings!
import { APPS } from 'some-module';
I have a CommonJS module, called inner.js, that defines a function and then exports that function as the entire module:
// inner.js, a legacy CommonJS module
var foo = function() { return 42; };
module.exports = foo;
In Node, I can readily verify this works as-is.
> var inner = require('./inner.js');
> inner() // prints 42
But that’s a legacy module that I’d like to use from a ES6 module, called outer.js:
// outer.js, an ES6 module
import * as inner from "./inner.js";
export function bar() { return inner(); }
I see that rollup-plugin-commonjs is commonly used in these situations but I can’t get it to work when the CommonJS inner.js module exports a function as the whole module. If, after running rollup and dumping the result to loadme.js, I try to run load the ES6 outer module and try to call the function originally defined in the inner CommonJS module, I get an error:
> var outer = require('./loadme.js')
undefined
> outer.bar()
TypeError: inner$2 is not a function
at Object.bar (/.../so-rollup-question/loadme.js:27:25)
I think I’m just failing to load the CommonJS module correctly, in such a way that the module itself functions as a function. I’m not familiar enough with UMD to get anything meaningful out of inspecting the rollup-output.
The rest of this post is about a minimum example.
Here’s my very simple index.js:
// index.js
export {bar} from "./outer.js";
which is read by my rollup config:
// rollup.config.js
import npm from "rollup-plugin-node-resolve";
import commonjs from 'rollup-plugin-commonjs';
export default {
entry : "index.js",
format : "umd",
moduleName : "sphereModule",
plugins : [ npm({jsnext : true}), commonjs() ],
dest : "loadme.js"
};
I have a complete clonable repository demonstrating the problem.
Assigning directly to module.exports is basically equivalent to having a default export. Hence importing the module as follows should work:
import inner from "./inner.js";
I'm writing a browser api with es6 (translated with babel). Since other js are going to call my api, I need to make my api accessible from the global (window) scope.
With module pattern in plain js (es5) I would have done something like this:
myApp.js
var myApp = (function(){
var a, b, c;
function setA(){
// set a
}
// other functions
return {
"setA": setA,
// ... other functions
};
}());
myAppExt.js
window.myApp = window.myApp || {};
(function(app){
app.Module1 = (function(){
return {
// methods of this module
};
}());
}(myApp));
With es6 we're not supposed to do something like this but to achieve the same objective I'm writing my app in this way:
myApp.js
import method1 from './common/module1.js'
import init from './folder/init.js'
import {method2, method3} from './folder/module2.js'
import log from './common/log.js'
const myApp = {};
window.myApp = myApp;
myApp.method1 = method1;
myApp.method2 = method2;
myApp.method3 = method3;
myApp.log = log;
init();
Is this the best way to achieve this goal or is there any better design solution?
If you are going to develop a library you will probably end up generating one single bundled file which contains all the contents of your library. To create a a a bundle you need a tool like webpack or browserify, both tools allow you to create your library in a way that can be consumed in many ways (AMD, CommonJS, global...).
So you need to create a root module:
myLibrary.js
import something from './framework/module1.js';
import somethingElse from './framework/module2.js';
// export public parts of your library
export {something};
export {somethingElse };
Then use webpack library setting:
{
output: {
// export itself to a global var
libraryTarget: "var",
// name of the global var: "Foo"
library: "Foo"
},
externals: {
// require("jquery") is external and available
// on the global var jQuery
"jquery": "jQuery"
}
}
More info here.
You can also use browserify standalone setting:
--standalone -s Generate a UMD bundle for the supplied export name.
This bundle works with other module systems and sets the name
given as a window global if no module system is found.
More info here.
I've actually merged the solution proposed from OweR ReLoaDeD with another I've found.
After configuring webpack to export global variable, instead of importing and then exporting methods I've exported directly what I needed to be available in the public api.
export {method} from './common/file1.js';
export * from './dir/file2.js'
export {anothermethod} from './common/file2.js
Thank you for the help
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.