8

I'd like to make a Javascript file that

  • exports its content (e.g. a class) if it can export (e.g. it has been loaded with <script type="module">)
  • and otherwise, assigns its content into the global variable such as window and global.

For example, let's assume such a file print.js.

Case A

One can use it like:

<script type="module">
    import print_things from "./print.js";
    print_things("Javascript innovation");
</script>

Case B

or,

<script src="./print.js"></script>
<script>
    print_things("Hmmmmmmm.");
</script>

Currently, using export makes the script throw an error in Case B: Uncaught SyntaxError: Unexpected token export. So it has to know whether export is available on the environment whereon it runs, in order to support the both use cases. How do I do this?

3 Answers3

1

Check out UMD (universal module definition). Namely, this example

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['exports', 'b'], function (exports, b) {
            factory((root.commonJsStrictGlobal = exports), b);
        });
    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
        // CommonJS
        factory(exports, require('b'));
    } else {
        // Browser globals
        factory((root.commonJsStrictGlobal = {}), root.b);
    }
}(typeof self !== 'undefined' ? self : this, function (exports, b) {
    // Use b in some fashion.

    // attach properties to the exports object to define
    // the exported module properties.
    exports.action = function () {};
}));
xavdid
  • 4,877
  • 2
  • 21
  • 30
1

Browsers that understand type=module should ignore scripts with a nomodule attribute. This means you can serve a module tree to module-supporting browsers while providing a fall-back to other browsers.

<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>
xjmdoo
  • 1,498
  • 8
  • 14
1

(Note: you probably shouldn't use this in the real world, but it is totally valid and does exactly what you want)
An implementation of your print.js:

function print_things(msg) {
   console.log(msg)
}
y=>0<!--y; export default print_things

<script type=module>
   // (renamed to avoid name collision)
   import print_things2 from "https://12Me21.github.io/external/print.js"
   print_things2("Javascript innovation")
</script>

<script src="https://12Me21.github.io/external/print.js"></script>
<script>
   print_things("Hmmmmmmm.")
</script>
Browsers will treat <!-- as a javascript line comment, unless it's in a module script. You can use this to "hide" code from non-modules, by adding a few extra chars to avoid a syntax error. In this example, the last line is parsed as either:
  • Normal script: y => 0(function) <!-- y; export ...(comment, ignored)
  • Module script: y => 0 < ! --y ;(function) export ...(export statement)

This is guaranteed to work in web browsers, but not node.js etc.
Here is the relevant part of the specification:

B.1.1 HTML-like Comments

... this extension is not allowed when parsing source text using the goal symbol Module: ...

12Me21
  • 741
  • 8
  • 18