Search code examples
angulartypescriptsystemjs

Exports is not defined


I'm trying to produce an Angular app based on my first app, which gives me some trouble.

Here's what I've got:

index.html

<!DOCTYPE html>
<html>
<head>
    <base href="/"/>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>FiveRX 2.0 Management</title>
    <meta charset="utf-8"/>

    <!-- Dependencies -->
    <script src="/node_modules/core-js/client/shim.min.js"></script>
    <script src="/node_modules/systemjs/dist/system.js"></script>
    <script src="/node_modules/zone.js/dist/zone.js"></script>

    <!-- Init Application -->
    <script src="/app/config/system.config.js"></script>
    <script src="/app/config/system.init.js"></script>

</head>
<body>
<div class="header">
    <div class="title">Troubleshooting</div>
</div>
<layout></layout>
</body>
</html>

system.config.js

(function (global) {
    System.config({
        paths: {
            "npm:": "node_modules/"
        },

        map: {
            "app": "app",
            "@angular/core": "npm:@angular/core/bundles/core.umd.js",
            "@angular/common": "npm:@angular/common/bundles/common.umd.js",
            "@angular/compiler": "npm:@angular/compiler/bundles/compiler.umd.js",
            "@angular/platform-browser": "npm:@angular/platform-browser/bundles/platform-browser.umd.js",
            "@angular/platform-browser-dynamic": "npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js",
            "@angular/http": "npm:@angular/http/bundles/http.umd.js",
            "@angular/router": "npm:@angular/router/bundles/router.umd.js",
            "@angular/forms": "npm:@angular/forms/bundles/forms.umd.js",
            "@angular/animations": "npm:@angular/animations/bundles/animations.umd.js",
            "@angular/animations/browser": "npm:@angular/animations/bundles/animations-browser.umd.js",
            "@angular/platform-browser/animations": "npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js",
            "rxjs": "npm:rxjs",
        },
        packages: {

            app: {
                main: "bootstrap.js"
            },
            rxjs: {
                defaultExtension: "js"
            },
            scripts: {
                format: "register",
                defaultExtension: "js",
                scriptLoad: true
            }
        },
        meta: {
            "*": {
                authorization: true
            }
        }
    });
})(this);

system.init.js

System.import('app')
    .catch(function(err) {
         console.error(err);
    });

bootstrap.ts

import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false,
        "lib": [ "es5", "es6", "es2015", "dom" ]
    }
}

As you see, the bootstrap file doesn't contain anything but an import of platform-browser-dynamic. I get the following error when I try to run this:

Error: exports is not defined
  Evaluating http://localhost:55549/app/bootstrap.js
  Loading app
    at eval (:55549/app/bootstrap.js:2)
    at eval (<anonymous>)
    at _e (evaluate.js:106)
    at instantiate.js:414
    at R (register-loader.js:684)
    at E (register-loader.js:631)
    at O (register-loader.js:540)
    at register-loader.js:128
    at ZoneDelegate.invoke (zone.js:365)
    at Zone.run (zone.js:125)
    at zone.js:760
    at ZoneDelegate.invokeTask (zone.js:398)
    at Zone.runTask (zone.js:165)
    at drainMicroTaskQueue (zone.js:593)
    at <anonymous>

Now, I can get this to work by targetting ES6 instead of ES5, but that's neither an explanation nor an option. Any ideas what's up here?


Solution

  • You've run into a rather peculiar situation.

    With your configuration, tsc will compile modules to the CommonJS format, which is the default if your target is not es6. So your bootstrap.ts code compiles to this JavaScript:

    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    //# sourceMappingURL=bootstrap.js.map
    

    SystemJS is normally able to recognize that a module is a CommonJS module and will automatically provide an execution environment suitable for this case. Unfortunately, the generated code above is not enough to get SystemJS to recognize that your module is in the CommonJS format and so it default to its native module format and fails on Object.defineProperty because exports does not exist.

    In the code above, you'll notice that there is no require call that corresponds to your import statement. That's because tsc detects that there is no need for a require call at runtime, and thus does not emit it. If you just change your TypeScript code to this, then tsc will emit a require call (otherwise the platformBrowserDynamic() call would fail) and SystemJS can detect the module format:

    import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
    
    platformBrowserDynamic();
    

    In cases where SystemJS is still unable to detect the format, remember that you can use the format option in your SystemJS configuration to tell SystemJS to use a specific format.

    When you change the target to es6, the default module format changes to es6 too, which explains why you don't get that error anymore.