Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Babel.js using Import and Export not working

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';
like image 994
icode Avatar asked May 09 '17 00:05

icode


2 Answers

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.

like image 120
mikeapr4 Avatar answered Sep 19 '22 22:09

mikeapr4


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.

like image 23
Jules Avatar answered Sep 17 '22 22:09

Jules