Can't require() default export value in Babel 6.x
TL;DR
You have to use
const app = require('./app').default;
app();
ExplanationBabel 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 thisimport { 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 doconst app = require('./app').default;
app();
Or, more concisely, and closer to your original code:require('./app').default();
Importing vs Requiring with Babel in Node
TL;DR
This is because importing withimport
is different than require
. When you import a module with the syntax import X from "Y"
, the default export is automatically imported, as the syntax is meant to import the default export by specification. However, when you require
, the default export is not automatically imported like you'd expect. You must add .default
to get the default export with require
like: require("./foo").default
.Babel Transpilation
Take a look at how Babel transpiles some example code:export { x }
Becomes:"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.x = x;
This makes sense as x
is just exported normally. Then you would proceed to do:require("module").x;
To receive x
. Similarly, try the following:export default new Foo();
That becomes:"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = new Foo();
You'll see that the default export is attached to the exports
object as a property named default
, just as x
was exported with property x
. That means, to get the default export with require
, you'll need to do:require("module").default;
Now to explain the why it's undefined
. In the line:var Foo = require("./foo");
Foo
here is actually just an object with a default
property:{
default: //Whatever was exported as default
}
Thus, trying to do Foo.bar
will yield undefined
because there is no bar
property. You need to access the default
property to access your instance.You might think this is a little clunky, and other do too. That's why there's a plugin to get rid of the need for the extra .default
. The plugin does module.exports = exports["default"]
to assign the module.exports
from ES5, to exports["default"]
.1
Difference Between import
and require
For import
syntax, Babel does the following transpilation:import Foo from "./foo";
Foo.bar();
Becomes:"use strict";
var _foo = require("./foo");
var _foo2 = _interopRequireDefault(_foo);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_foo2.default.bar();
To break it down line by line, Babel just require
s the module. Then, it calls _interopRequireDefault
on the module. A check is done with obj && obj.__esModule
to make sure that the module actually exports anything and the module is exported with ES2015/ES6 syntax. If it is, then the module is returned as is, or else { default: obj }
is returned. This is to ensure that module.exports = x
in ES5 is treated the same as export default x
in ES2015/ES6. You'll notice that for default import syntax, Babel automatically adds .default
to retrieve the default export.However, the corresponding code with require
:
var Foo = require("./foo");
Foo.bar();
Is completely valid ES5 code, so nothing is transpiled. Consequently, .default
is never added to Foo
when it is used. Thus, the default export is not retrieved.Notes
1It should be noted thatmodule.exports
and exports
just refer to the same object, see this answer. module.exports
holds the data which is exported from the module. The reason why module.exports = x
successfully exports x
as default and does not need the extra .default
on require
is because you're assigning the module.exports
to one single thing. Since module.exports
holds the data that's imported, require("module")
imports whatever module.exports
is, which is x
. If module.exports
were 3, then require("module")
would be 3. If module.exports
were an object like { blah: "foo" }
, then require("module")
would be { blah: "foo" }
. By default, module
and module.exports
are objects.One the other hand, exports.default
exports an object (that refers to the same object as module.exports
) with a default
property that holds the default export. You may want to do exports = x
to export x
as default but since exports
is assigned a reference to module.exports
this will break the reference and exports
will no longer point to module.exports
.
module.exports vs. export default in Node.js and ES6
The issue is with
- how ES6 modules are emulated in CommonJS
- how you import the module
ES6 to CommonJS
At the time of writing this, no environment supports ES6 modules natively. When using them in Node.js you need to use something like Babel to convert the modules to CommonJS. But how exactly does that happen?Many people consider module.exports = ...
to be equivalent to export default ...
and exports.foo ...
to be equivalent to export const foo = ...
. That's not quite true though, or at least not how Babel does it.
ES6 default
exports are actually also named exports, except that default
is a "reserved" name and there is special syntax support for it. Lets have a look how Babel compiles named and default exports:
// input
export const foo = 42;
export default 21;
// output
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var foo = exports.foo = 42;
exports.default = 21;
Here we can see that the default export becomes a property on the exports
object, just like foo
.Import the module
We can import the module in two ways: Either using CommonJS or using ES6 import
syntax.
Your issue: I believe you are doing something like:
var bar = require('./input');
new bar();
expecting that bar
is assigned the value of the default export. But as we can see in the example above, the default export is assigned to the default
property!So in order to access the default export we actually have to do
var bar = require('./input').default;
If we use ES6 module syntax, namelyimport bar from './input';
console.log(bar);
Babel will transform it to'use strict';
var _input = require('./input');
var _input2 = _interopRequireDefault(_input);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
console.log(_input2.default);
You can see that every access to bar
is converted to access .default
. Why can't I import a default export with import ... as with BabelJS
You can import the default export by either
import Test2 from './test';
orimport {default as Test2} from './test';
The default export doesn't have Test
as a name that you would need to alias - you just need to import the default under the name that you want.The best docs I've found so far is the article ECMAScript 6 modules: the final syntax in Axel Rauschmayers blog.
Mixed default and named exports in Node with ES5 syntax
You want to assign the value of module.exports
to be your default function, and then put all the named exports as properties on that function.
const defaultFunction = () => { console.log('default!'); };
const namedFunction1 = () => { console.log('1!'); };
const namedFunction2 = () => { console.log('2!'); };
const myModule = module.exports = defaultFunction;
myModule.namedFunction1 = namedFunction1;
myModule.namedFunction2 = namedFunction2;
Let's say that was in myModule.js
. Then you can do this:const myModule = require('./myModule');
myModule(); // Prints: 'default!'
myModule.namedFunction1(); // Prints: '1!'
Re-export default in ES 6 modules
If you use proposal-export-default-from
Babel plugin (which is a part of stage-1
preset), you'll be able to re-export default using the following code:
export default from "./App.js"
For more information see the ECMAScript proposal.Another way (without this plugin) is:
export { default as App } from "./App.js"
The above is a very common practice when separate files, each with its own
export
, have all something in common, for example, utils
, so if, for example, one would want to import 3 utility functions, instead of having to write multiple imports:import util_a from 'utils/util_a'
import util_b from 'utils/util_b'
import util_c from 'utils/util_c'
One could import any of the utilities in a single-line:import { util_a, util_b , util_c } from 'utils'
By creating an index.js
file in the /utils
folder and import all the defaults of all the utilities there and re-export, so the index
file will serve as the "gateway" for all imports related to that folder. The difference between require(x) and import x
This simple image will help to you understand the differences between require
and import
.
Apart from that,
You can't selectively load only the pieces you need with require
but with import
, you can selectively load only the pieces you need, which can save memory.
Loading is synchronous(step by step) for require
on the other hand import
can be asynchronous(without waiting for previous import) so it can perform a little better than require
.
SyntaxError: Cannot use import statement outside a module
Verify that you have the latest version of Node.js installed (or, at least 13.2.0+). Then do one of the following, as described in the documentation:
Option 1
In the nearest parent package.json
file, add the top-level "type"
field with a value of "module"
. This will ensure that all .js
and .mjs
files are interpreted as ES modules. You can interpret individual files as CommonJS by using the .cjs
extension.
// package.json
{
"type": "module"
}
Option 2Explicitly name files with the .mjs
extension. All other files, such as .js
will be interpreted as CommonJS, which is the default if type
is not defined in package.json
.
Why does require not behave the same as import when same structure is given
You import
the default export of module and require
the module itself.
const ExamplePost = require(`${postName}.md`).default
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/importAlso check Can't require() default export value in Babel 6.x
Related Topics
Can Jquery Provide the Tag Name
Conversion Between Utf-8 Arraybuffer and String
Create an Array with Random Values
Why Do I Need to Await an Async Function When It Is Not Supposedly Returning a Promise
Why Is Usestate Not Triggering Re-Render
Detect Whether Scroll Event Was Created by User
Create File with Google Drive API V3 (Javascript)
How to Reset the Scale/Zoom of a Web App on an Orientation Change on the Iphone
Scroll Smoothly to Specific Element on Page
JavaScript - Generating All Combinations of Elements in a Single Array (In Pairs)
How Do Cors and Access-Control-Allow-Headers Work
How to Remove a Table Row with Jquery
How to Set the Style -Webkit-Transform Dynamically Using JavaScript
Set Window to Fullscreen (Real Fullscreen; F11 Functionality) by JavaScript
Javascript: Listen for Attribute Change
Window.Location.Reload with Clear Cache
Jsf/Primefaces Ajax Updates Breaks Jquery Event Listener Function Bindings