// a.js
export const a = 'a';
// A.js
export const A = 'A';
// index.js
import {a} from './a';
import {A} from './A';
javascript is allowed to create es6 modules differing only in letter case?
This depends much on your module loader/bundler and file system.
ES6 considers these as two different module specifiers for sure. It's possible that they resolve to the same module, but unlikely.
In any case, I would recommend to stay away from this, to avoid confusion.
Related
While reading about tree shaking in webpack documentation, I came across this sentence:
In a 100% ESM module world, identifying side effects is straightforward. However, we aren't there just yet.
What do they mean by "100% ESM module" and how it is different from the current import and export that we already use today?
reference: https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free
The documentation you're reading is contrasting two types of scripts:
Scripts which expose everything they do through what they import and export
Scripts which do something in addition to importing and exporting (this could be seen as a "side effect")
Consider a big library, one that installs itself as a namespace on the global object to expose its functionality. Let's say that jQuery did this - that is, that it runs something like
const jQuery = (function() {
// lots and lots of code
})();
export default jQuery;
window.jQuery = jQuery;
This contains a side-effect: the line
window.jQuery = jQuery;
This means that other parts of your application could use window.jQuery without jQuery being specifically imported into that module. For example, you might have
// someModule.js
export const doStuff = () => {
window.jQuery('div').text('Hi!');
};
And this can work without the line
import jQuery from 'jQuery';
inside the module script, because jQuery is on the window. (For this to work, there needs to be at least one module somewhere that does import 'jQuery'; or something like that, so that jQuery's code that assigns itself to the window runs)
Because of the side-effect, Webpack will have a harder time with automatic tree-shaking - you'll have to explicitly note which modules depend on modules with side-effects.
In contrast, a module without dependency side-effects would be the someModule.js example above: all it does it export a function, without adding or changing functionality elsewhere.
So by "100% ESM module", Webpack is probably referring to scripts for which all modules' dependencies are explicit with import statements, instead of having to depend on side-effects (like a non-imported module assigning something to the window).
There are two popular module syntax nodejs use.
Commonjs: https://nodejs.org/dist/latest-v14.x/docs/api/modules.html
// exporting
module.exports.a = 1
// or, exports is an alias to module.exports, for all differences check out docs
exports.a = 1
// you can assign module.exports object as well, this sets what's exported
module.exports = {
b: 2
}
// a is not exported anymore
// importing default import, imports module.exports object
const a = require('./b')
// or via named import
const {c} = require('./b');
ES modules: https://nodejs.org/dist/latest-v14.x/docs/api/esm.html
// names export
export const a = 1;
// default export
export default const b = 2;
// importing via name
import {a} from './c'
// importing default export
import c from './b'
Commonjs and esm are still in use. So we are not in %100 esm world yet.
tealium-tracker is written in es6 and transpiled using Babel before published to npm.
When consumers do:
import initTealiumTracker from "tealium-tracker";
everything works as expected.
However, some consumers want to use a require instead of an import, and have to append .default:
const initTealiumTracker = require("tealium-tracker).default;
How could I publish the library to avoid appending .default?
I want consumers to be able to do either:
import initTealiumTracker from "tealium-tracker";
or
const initTealiumTracker = require("tealium-tracker);
Source code
In your source code, If you are ok with using commonJS syntax for import and export...
One option would be to replace all import and export with require and module.exports. Looks like webpack doesn't allow mixing the syntaxes (ES6 and commonJS modules).
So your index.js file can require the functions from dependent module as
const { callUtag, flushUtagQueue } = require("./utagCaller");
and export the default function as
module.exports = initTealiumTracker;
module.exports.default = initTealiumTracker;
Likewise your dependent module can export the functions as
module.exports = { callUtag, flushUtagQueue };
This way, consumers should be able to use either
import initTealiumTracker2 from "tealium-tracker";
OR
const initTealiumTracker1 = require("tealium-tracker");
Context
JavaScript has two types of exports: normal[1] and default.
EDIT: JavaScript has two types of export syntaxes
Normal exports
class Foo {...}
class Bar {...}
export {
Foo,
Bar,
};
or
export class Foo {...}
export class Bar {...}
Normal exports can be imported in two ways: namespace[1] imports and named imports (looks similar to destructuring).
Namespace imports
import * as Baz from './baz';
Named imports
import {Foo, Bar} from './baz';
Default exports
class Foo {...}
class Bar {...}
export default {
Foo,
Bar,
};
Default exports can also be imported in two ways: namespace imports and named imports (in a hackish way using destructuring in a separate statement)
Namespace imports
import Baz from './baz';
Named imports
import temp_Baz from './baz';
const {Foo, Bar} = temp_Baz;
Question
Both normal exports and default exports have the same functionality - they can be imported into a namespace, and they can be destructured into smaller pieces.
1) What are the reasons JavaScript has default exports, instead of sticking with just normal exports and having the import syntax be the following?
import <varName> from <location>;
<varName> would allow destructuring like any variable assignment, without a special "named" import.
Node.js managed without default exports, and the only additional change would be to allow exporting of a single value:
module.exports = class Foo {...}
This could be done with export = or export only or something similar:
export only class Foo {...}
So now you might ask, what is the difference between export only and export default?
export only exports could be imported with the same syntax, whereas export default requires a different importing syntax. To allow both syntaxes to work for the module user, the module must both export and export default the same items (which is something I do in all my modules).
export default is renamed to export only which is a better term in my opinion (less confusing; clearer)
2) All the differences listed above are pros. Are there any other differences that are cons? Or can any of the above differences also be cons?
Edit
It seems I misunderstood the intended use for export default.
It's for adding a default export on top of the regular exports.
So I'd now like to know when are default exports used? Although I probably shouldn't add more to this question.
If the only use for default exports is if your module only has a single item to export, then this question still applies.
Edit 2
Now it seems that the intended use is if a module only exports a single item. So my question still applies. Why not replace default exports with export only, removing the need for an additional default import syntax?
Notes
[1] I am not sure if this is the correct term to use
The existence of "default exports" as well as "named exports" is due to design by committee. This was convincingly argued by Andreas Rossberg during the original design discussions:
Some want modules in the conventional sense, as encapsulated namespaces with named exports, which you can import and access qualified or unqualified, and where imports are checked.
Some want modules in the specific pre-ES6 JavaScript style, where they can be any arbitrary JS value, and in the good old JavaScript tradition that checking doesn't matter.
The current design (including the newest suggestions for imports) seems to please neither side, because it tries to be the former semantically, but wants to optimise for the latter syntactically. The main outcome is confusion.
That is, the motivation for "default exports" was not technical, but political, designed to appease those in the CommonJS and AMD worlds, where a module exports a single value. This neatly explains why we have two module systems in one; something we don't see in other programming languages.
(Thanks a lot to Bergi, who found and cited this thread in his answer. However, I disagree with his conclusion that default exports are motivated by a need for a shorthand. I don't see much discussion of this in the original threads.)
JavaScript has two types of exports: normal and default.
No, not really. JavaScript has only one kind of export structure. It has different syntaxes though.
export default function x() {} is just a shorthand notation for function x(){}; export { x as default }.
import x from '…' is just a shorthand notation for import { default as x } from '…'.
The difference between your the two module exports you discussed is much bigger than the single default keyword. They only look so similar because you used shorthand notations. Fully spelled out, it's
export {
Foo as Foo,
Bar as Bar,
} // a declaration
vs
export default ({
Foo: Foo,
Bar: Bar,
}); // an object literal
Both normal exports and default exports have the same functionality - they can be imported into a namespace, and they can be destructured into smaller pieces.
No, they don't. Imports provide aliases for the exported variable declarations, there's no destructuring - and you cannot do that to properties of a default-exported object.
What are the reasons JavaScript has default exports, instead of sticking with just normal exports and having the import syntax be the following?
See https://esdiscuss.org/topic/moduleimport. The default-export syntax provides a shorthand syntax for exporting under the special name default (which is not a valid variable name otherwise), as it is a quite common use case to export only a single thing from a module. It doesn't require explicitly naming a value when there is only one in this module anyway. In this regard, it has very much the purpose you envisioned for your export only suggestion, but it is only one export not providing a full namespace of multiple aliasable exports.
export default {
Foo,
Bar,
}
I don't understand why you've demonstrated this structure. This isn't how it's supposed to be used.
The default export really be though of as just another export. If you import * as Package from "package" you'll realise... Package.default is the default export
The only change is the syntactic shortcut:
import alias from "package" = import {default as alias} from "package"
It makes consumption of many packages easier / simpler.
export = ... is not the same as export default ...
The prior returns a module structure of ... where as latter returns a module structure of {default: ...}
My company is using Webpack for a large project with thousands of modules. We use classes extensively, and because classes are not hoisted in ES6, recursive dependency issues occur frequently.
Here’s a simplified scenario:
// a.js ////
import {B} from './b';
export class A {
isB(b) {
return b instanceof B;
}
}
// b.js ////
import {A} from './a';
export class B extends A {}
// index.js ////
import './a';
See a running example of the problem.
In this scenario, the desired execution order is b > a > index, because b has a static dependency on a , while a can defer evaluating b. Naturally, however, Webpack doesn't know this (nor should it, per the spec).
I’d like to write a Webpack plugin and/or loader that lets me do the following:
import {B} from 'defer!./b';
The defer loader would cause a's dependency on b to be added to the dependency graph, but to be ignored for the purposes of ordering module execution.
Is there an API that plugins or loaders to can access to accomplish this? I didn't see anything in the docs. Any help or suggestions would be appreciated.
Is it ok in TypeScript to mix these too 'include' types?
import { SomeType, someFunc } from "./MyThings";
And:
import events = require('events');
The first would be a TypeScript module with an export or two... and the second is just a node module called events (I guess this could be anything pulled in from NPM too).
Am I ok to mix these two conventions in a single TypeScript file?
Yes, this is acceptable in TypeScript.
When using the import foo = require('foo');, you must have a module 'foo' declared with declare module 'foo', typically in a .d.ts file.
This is typically covered in node.d.ts.