In many pieces of code I saw such expression:
require('babel-polyfill').default;
What does it mean property default, and where I can find all properties that could be applied to babel-polyfill, because I didn't see in Babel official documentation usage of this option.
This is an ES6 module convention, where someone is setting the "default" export of the module to a specific object. In ES6 syntax, it's equivalent to:
import Module from 'babel-polyfill'
which will take the default export from babel-polyfill and put it in your current file as Module.
And internally in the babel-polyfill library, they are doing
exports.default = { some: 'Object' }
This is different than named exports, where you want to expose specific named things from your library:
exports.someThing = 'value';
...
import { someThing } from 'that-module';
You can console.log the results of both require('babel-polyfill') and require('babel-polyfill').default to see more. However, babel polyfill mainly provides polyfills in the global namespace, and modifies native prototypes like Array, and you don't use anything from it directly. Simply requiring it has side effects that add correct polyfills to the running Javascript environment.
Related
You can see my sample project here: https://github.com/DanKaplanSES/typescript-stub-examples/tree/JavaScript-import-invalid
I have created this file called main.ts:
import uuid from "uuid";
console.log(uuid.v4());
Although typescript is fine with this import, when I try to node main.js, it gives this error:
console.log(uuid_1["default"].v4());
^
TypeError: Cannot read property 'v4' of undefined
at Object.<anonymous> (C:\root\lib\main.js:5:31)
←[90m at Module._compile (internal/modules/cjs/loader.js:1063:30)←[39m
←[90m at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)←[39m
←[90m at Module.load (internal/modules/cjs/loader.js:928:32)←[39m
←[90m at Function.Module._load (internal/modules/cjs/loader.js:769:14)←[39m
←[90m at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)←[39m
←[90m at internal/main/run_main_module.js:17:47←[39m
If I change the file to this, it executes fine:
import * as uuid from "uuid";
console.log(uuid.v4());
If the first version is invalid, why doesn't typescript inform me?
I have a multi file tsconfig setup. Check the github project for more details, but here are the shared compiler options which may be relevant:
{
"compilerOptions": {
"rootDir": ".",
"esModuleInterop": true,
"module": "CommonJS",
"moduleResolution": "node",
"composite": true,
"importHelpers": true,
},
}
Here is how the main.js looks:
doesn't work
"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var uuid_1 = tslib_1.__importDefault(require("uuid"));
console.log(uuid_1["default"].v4());
works
"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var uuid = tslib_1.__importStar(require("uuid"));
console.log(uuid.v4());
I kind of feel guilty answering my own bounty question, so I'm going to mark it as community. The reason I'm writing my own is because I feel like the other answer really buries the lead. I had to perform hours and hours of my own research after reading it to be able to write this. That being the case, I think my answer will be more helpful to people that start in my boat, where I didn't know what I didn't know. I also think there's an additional solution to the problem, though it would be more invasive.
My original question was, "If the first version is invalid, why doesn't typescript inform me?" Here is the other answer's explanation:
Because you have enabled esModuleInterop which also enables allowSyntheticDefaultImports. The CommonJS bundle is actually incompatible with that option but TypeScript doesn't know.
This is absolutely true, but when it comes to understanding what's going on, it's the tip of the iceberg:
If you look at the reference documentation, it recommends you set esModuleInterop to true. Why would it make that recommendation if it reduces type safety? Well, that is not why it recommends you set it to true. In fact, this setting does not reduce type safety -- it increases it by fixing some legacy typescript bugs, specifically two that deal with how typescript handles requires. You can read the documentation for more details on that, but in my opinion, if you are using node libraries, I think it's a good idea to set esModuleInterop to true.
But! esModuleInterop has a side effect. At the very bottom of its documentation, it says:
Enabling esModuleInterop will also enable allowSyntheticDefaultImports.
Err... kinda. IMO, this documentation is incorrect. What it should really say is, "Enabling esModuleInterop will default allowSyntheticDefaultImports to true." If you look at the allowSyntheticDefaultImports documentation, it says this on the right-hand side:
Hey, notice how in that upper right-hand corner it doesn't say this setting is recommended? That's probably because this setting reduces type safety: it allows you to type import React from "react"; instead of import * as React from "react"; when the module does not explicitly specify a default export.
Normally (i.e. with allowSyntheticDefaultImports set to false), this would be an error... because it is: you shouldn't be able to default import a module unless it has a default export. Setting this to true makes the compiler say, "Nah, this is fine."
But, when you set allowSyntheticDefaultImports to true, "this flag does not affect the JavaScript emitted by TypeScript." What this means is, this flag lets you pretend the library was written one way at compile time even though it wasn't. At runtime, this will error. Why does the setting even exist? I have no idea, but it probably has to do with historical reasons:
This option brings the behavior of TypeScript in-line with Babel, where extra code is emitted to make using a default export of a module more ergonomic.
It seems like there was(/is?) a point in time where everybody was just assumed to be using Babel. I am not doing that, so the "ergonomic" benefit becomes a runtime error.
As a cleaner method, you should import uuid with import { v4 } from 'uuid';
True, but I think it would also be a good idea to explicitly set allowSyntheticDefaultImports to false. It gives you more type safety. Not only that, it makes import uuid from "uuid"; a compile time error (which it should be).
There is one more thing I don't understand:
Setting allowSyntheticDefaultImports to false also makes imports like import os from "os"; and import _ from "lodash"; compile time errors. But those always ran fine when allowSyntheticDefaultImports was true. There must be some piece I'm missing that explains why those work but uuid doesn't.
I can't find the source of os in my node_modules, but I can look at lodash, and its index.js does this:
module.exports = require('./lodash');
In that required file, it says this at the bottom:
...
/*--------------------------------------------------------------------------*/
// Export lodash.
var _ = runInContext();
// Some AMD build optimizers, like r.js, check for condition patterns like:
if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
// Expose Lodash on the global object to prevent errors when Lodash is
// loaded by a script tag in the presence of an AMD loader.
// See http://requirejs.org/docs/errors.html#mismatch for more details.
// Use `_.noConflict` to remove Lodash from the global object.
root._ = _;
// Define as an anonymous module so, through path mapping, it can be
// referenced as the "underscore" module.
define(function() {
return _;
});
}
// Check for `exports` after `define` in case a build optimizer adds it.
else if (freeModule) {
// Export for Node.js.
(freeModule.exports = _)._ = _;
// Export for CommonJS support.
freeExports._ = _;
}
else {
// Export to the global object.
root._ = _;
}
I don't really understand what all of this is doing, but I think this is defining a global variable named _ at runtime? I guess that means, from a typescript perspective, this coincidentally works out. The type declaration files don't have a default, which would normally cause a runtime error, but almost coincidentally, it all works out in the end because the lodash javascript defines a global _? shrug Maybe this is a pattern that os uses, too, but I've already spent enough hours researching this, so I will leave that for another day/question.
Your issue is related to interoperability between TypeScript/ECMAScript modules and CommonJS.
When it comes to the differences between ECMAScript modules and CommonJS modules:
CommonJS modules are meant to be imported like const library = require('library') which allows to retrieve the full exports object of that library. There is no notion of default import in CommonJS
ECMAScript modules have explicit export clauses for every exported item. They also feature a default import syntax which allows to retrieve the default export in a local variable.
In order to implement interoperability between CommonJS modules and TypeScript's default import syntax, CommonJS modules can have a default property.
That default property can even be added automatically by TypeScript when esModuleInterop is enabled (which also enables allowSyntheticDefaultImports). This option adds this helper function at transpilation time:
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Basically what this function does is: if the imported module has the __esModule flag set to true, export it as is because the module is intended to be used as an ECMAScript module: import { feature } from 'library'. Otherwise, export it inside a wrapper object with a default property, which enables the import localName from 'library' syntax.
The uuid package is being built with #babel/plugin-transform-modules-commonjs which includes the __esModule flag and prevents you from using the default import syntax. Other packages like lodash don't include this flag, which allows TypeScript to add the default property.
As a conclusion, TypeScript provides options to interoperate with legacy CommonJS modules but these options don't work with "ECMAScript aware" CommonJS modules. TypeScript cannot warn or error out at transpilation time because a CommonJS module interface has no representation other than the exports object, which is only known at runtime.
First of all, I'm working under the ESM module environment.
I have been struggling with the uuid package import issue multiple times, in the past years.
Today I ran into it again, and found something new to me and like to share it under your great analytics answer.
TL;DR: the behavior of the import UUID from 'uuid' is different when the uuid package is dependent directly, or indirectly.
I think this is related to the ESM behavior.
package.json dependency with uuid
import * as UUID from 'uuid'
import UUID from 'uuid'
directly (tsc)
OK
OK
directly (runtime)
OK
SyntaxError: The requested module 'uuid' does not provide an export named 'default'
in-directly (tsc)
OK
Module "node_modules/#types/uuid/index" has no default export.ts(1192)
in-directly (runtime)
TypeError: UUID.v4 is not a function
OK
"directly dependency with uuid" means we have a uuid in the dependencies of package.json
"indirectly dependency with uuid" means we have no uuid in the dependencies of package.json (but other dependency module has included it)
Conclusion
It seems that there is only one situation in which the typing system and the runtime both work as expected with ESM: use a direct dependency to the uuid package.
Always npm install --save uuid. (see my issue here with commits)
I'm studying the new import, export feature in Javascript but was wondering, where in code will these statements be syntactically legal?
I understand something like the following won't be legal:
(function(){
import thing from './thing.js';
})();
but does this mean import is only legal at the top of the module script? Or in the global scope? E.g., what about this:
import a from './a.js';
(function(){
// ... do something with a ...
})();
import b from './b.js';
// ...
Also, does this limitation apply to export? E.g., will the following be legal?
(function(){
function internalFunc() {
// ...
}
export { internalFunc };
})();
I couldn't seem to find anything about this in the current drafts of the specification.
There is no such implementation in javascript. It's planned. But no browser implemented it yet. It's implemented in some transpilers like Webpack and Babel. There is also require in NodeJs. But not natively in javascript.
Other way to import files is using RequireJS library.
Reference: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/import
Edit
Answering what you asked in comments: AFAIU in the already available implementations of import and export, yes they are available in the global space, and yes import and export are hoisted.
But what isn't very clear in your comment's question is what you mean by "only available in global space". There is no such this as a close space that can't acess global space. Global space is accessible everywhere, so are import and export.
My reading of the spec is that:
module export statements should be at top level of a module
module import statements should be at top level of a module
function-style module import expressions (which return a promise for the imported items) are allowed anywhere an expression is allowed
As you say, right now it's only supported in transpilers, so I'm not sure how closely existing transpilers (Babel) follow these rules.
1) If you want just to play with import, export statements, then use it without any transpilation (with webpack) in google chrome ;)
I always use ES6 modules while I make some R&D. And then only if my temporarily work worth it, I start to think about transpilation.
Just do not forget to include scripts in such way:
<script type="module" src="index.js"></script>
2) If you need to write some nodejs script, then turn on some experimental flag to use modules - https://nodejs.org/api/esm.html#esm_enabling
I'm making a JavaScript library that can accept various plugins from an external library (cwl-svg). In this library I want to check what kind of plugin they are, so I use code along the lines of this:
import {SVGArrangePlugin} from "cwl-svg";
export default function myFunction(plugins){
for (plugin of plugins)
if (plugin instanceof SVGArrangePlugin)
doSomething();
Then, when I build my library, webpack adds the cwl-svg library to my bundle, as it should.
Now lets say the user of my library writes the following code:
import {SVGArrangePlugin} from "cwl-svg";
import func from "my-library";
func([new SVGArrangePlugin()]);
The problem is that when the user passes in a plugin to this function with new SVGArrangePlugin(), they are passing in an instance of the class from their own version of the cwl-svg library, because my library has its own bundled version. Thus, plugin instanceof SVGArrangePlugin always returns false, even though plugin is identical to an instance of that class.
How do I ensure plugin instanceof SVGArrangePlugin returns true using webpack? I considered having my library import cwl-svg using externals, but that seems to be for libraries that export themselves to the window object, when I would rather keep everything contained in my modules. Is there an obvious design decision I'm missing here?
externals is exactly what you want. It's not only useful for libraries that export something to the global object, webpack still declares it as an explicit dependency. From the docs:
The external library may be available in any of these forms:
root: The library should be available as a global variable (e.g. via a script tag).
commonjs: The library should be available as a CommonJS module.
commonjs2: Similar to the above but where the export is module.exports.default.
amd: Similar to commonjs but using AMD module system.
I'm trying to use import and export to create modules and it's not working.
I added https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.24.0/babel.min.js to the index.html header and tried to import a js file and get an error message saying SyntaxError: import declarations may only appear at top level of a module. What can I possibly be doing wrong?
I know I can use require.js but rather use import and export.
HTML
script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.24.0/babel.min.js"></script
JS File
import Mymodule from './modules/mymodule';
Babel cannot perform client-side transpiling of modules, or rather it is not universally supported by browsers. In fact, unless you use a plugin, Babel will transform import into require().
If I run the following code:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.24.0/babel.js"></script>
<script defer type="text/babel" data-presets="es2015">
import Mymod from './modules/module';
Mymod();
</script>
</head>
I get the following error:
Uncaught ReferenceError: require is not defined
From Babel Docs:
Compiling in the browser has a fairly limited use case, so if you are working on a production site you should be precompiling your scripts server-side. See setup build systems for more information.
Most people choose a pre-compiled module bundler like Webpack or Rollup.
If you really want to perform this client-side, use RequireJS with Babel run via a plugin, though you may need to use AMD syntax.
Native browser support for ES6 modules is still in early stages. But to my knowledge there isn't a preset/plugin available yet for Babel to tell it not to transform import/export statements.
The scripts that babel-standalone translates execute by default in global scope, so any symbols defined by them are automatically available to every other module. From that perspective, you don't need import/export statements in your modules.
However, you might be trying to maintain source files that can be used both by babel-standalone (e.g. for quick test environments, feature demonstrations, etc) and via bundlers such as webpack. In that case, you need to keep the import and export statements there for compatibility.
One way to make it work is to add extra symbols into the global scope that cause the import and export code that babel generates to have no effect (rather than causing an error as usually occurs). For example, export statements are compiled into code that looks like this:
Object.defineProperty (exports, "__esModule", {
value: true
});
exports.default = MyDefaultExportedClass;
This fails if there is no existing object called "exports". So give it one: I just give it a copy of the window object so anything interesting that gets defined is still accessible:
<script>
// this must run before any babel-compiled modules, so should probably
// be the first script in your page
window.exports = window;
import statements are translated to calls to require(). The result (or properties extracted from it) is assigned to the variable used as the identifier in the import statement. There's a little bit of complication around default imports, which are different depending on whether or not the result of require() contains the property __esModule. If it doesn't, things are easier (but then you can't support having both default and named exports in the same module ... if you need to do this, look at the code babel produces and figure out how to make it work).
So, we need a working version of require(). We can provide one by giving a static translation of module name to exported symbol/symbols. For example, in a demo page for a React component, I have the following implementation:
function require (module) {
if (module === "react") return React;
if (module === "react-dom") return ReactDOM;
}
For a module returning multiple symbols, you'd just return an object containing the symbols as properties.
This way, a statement like
`import React from "react";`
translates to code that is effectively:
`React = React;`
which is roughly what we want.
My code:
import $ from 'jquery'
import jQuery from 'jquery'
import owlCarousel from '../../node_modules/owlcarousel/owl-carousel/owl.carousel'
class App {
…
_initSlider() {
$("#partners-carousel").owlCarousel();
}
}
I have 'jQuery is not defined' in browser console. What's wrong?
I can use jQuery as $ in methods of this class, but not with name 'jQuery'.
According to this comment and apply it to your case, when you're doing:
import $ from 'jquery'
import jQuery from 'jquery'
you aren't actually using a named export.
The problem is that when you do import $ ..., import jQuery ... and then import 'owlCarousel' (which depends on jQuery), these are evaluated before, even if you declare window.jQuery = jquery right after importing jquery. That's one of the ways ES6 module semantics differs from CommonJS' require.
One way to get around this is to instead do this:
Create file jquery-global.js
// jquery-global.js
import jquery from 'jquery';
window.jQuery = jquery;
window.$ = jquery;
then import it in you main file:
// main.js
import './jquery-global.js';
import 'owlCarousel' from '../../node_modules/owlcarousel/owl-carousel/owl.carousel'
class App {
...
_initSlider() {
$("#partners-carousel").owlCarousel();
}
}
That way you make sure that the jQuery global is defined before owlCarousel is loaded.
#Serge You should have mentioned in your question that you are using browserify & babelify to bundle/transpile your code (I knew it from comments), this will help people find the correct answer to your question.
As of 2021, ECMA2015+/ES6+ don't allow the use of import-maps/bare-module-path natively in the browser. So basically you can't do the following directly in the browser, because the browser doesn't behave like nodejs, it doesn't understand how/where to fetch for the source of your scripts, you can't just say:
import $ from 'jquery'
import jQuery from 'jquery'
However, you can do this by the help of bundlers like WebPack which opens that door for import-maps/bare-module-path to be used in the browser. Besides, huge work is currently being done to support the implementation of import-maps directly in the browser without the need of bundlers, but it's not implemented yet. I know that this question is old enough for the OP to follow, but in general, you can use WebPack to bundle your code and import your dependencies the way you mentioned.
P.S. Regarding the answer proposed by #egel in Oct 2016 (which is an old answer with limited solutions at that time) some people asked for more clarifications. Please note the following statement by Nicolás Bevacqua regarding the scope of ES6+ modules:
Declarations in ES6 modules are scoped to that module. That means that any variables declared inside a module aren’t available to other modules unless they’re explicitly exported as part of the module’s API (and then imported in the module that wants to access them).
ES6+ module system is awesome and makes things much more organized, but can we fully implement ES6+ modules in the browser without the need of bundlers/transpilers? This is a tough question. Things may get harder when some of your JavaScript dependencies are just old/classic scripts that do not support the ES6+ modules system, and do not use the export keyword to export functions/values for you. Here, developers tend to do some workarounds to solve the problem in hand. The window object is used to attach functions/variables in order to use them across all modules. Here the window object is used as a carrier to transfer functions/data across different modules within your code base, and this is not a recommended approach though.
Below is quoted from javascript.info:
If we really need to make a window-level global variable, we can explicitly assign it to window and access as window.user. But that’s an exception requiring a good reason.