Javascript – How to fix “ReferenceError: exports is not defined” in a pure TypeScript project

javascripttypescript

If I write a .ts with any kind of import or export instruction the resulting .js will generate the following error when loaded in an HTML page: "ReferenceError: exports is not defined"

How to reproduce:

  1. Create a Blank Node.js web application project in VS
  2. Add a .ts with an import or export line
  3. Add some HTML calling the resulting .js
  4. Start an HTTP server (http-server -g [port]) and reach your HTML

I tried:

  • Targetting ES 5
  • Removing the line "module": "commonjs" from tsconfig.json`
  • Installing CommonJS and SystemJS
  • Compiling the .ts using tsc
  • Any other solution Stack Overflow has in similar questions
  • All the possible permutations of the above.

My .ts (can be anything if it has an import instruction):

import { setTimeout } from "timers";
setTimeout(() => console.log("asdf"), 1000);

The HTML just has a simple body referencing the script

My package.json

{
  "name": "nodejs-web-app4",
  "version": "0.0.0",
  "description": "NodejsWebApp4",
  "main": "server.js",
  "author": {
    "name": ""
  },
  "devDependencies": {
    "@types/node": "^8.0.14"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "lib": ["es6"],
    "sourceMap": true
  },
  "exclude": [
    "node_modules"
  ]
}

Resulting .js:

From VS build (results in ReferenceError: exports is not defined):

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const timers_1 = require("timers");
timers_1.setTimeout(() => console.log("asdf"), 1000);
//# sourceMappingURL=home-script.js.map

From command tsc [filename].ts (results in ReferenceError: exports is not defined):

"use strict";
exports.__esModule = true;
var timers_1 = require("timers");
timers_1.setTimeout(function () { return console.log("asdf"); }, 1000);

From VS Build but removing "module": "commonjs" from tsconfig (results in SyntaxError: import declarations may only appear at top level of a module):

import { setTimeout } from "timers";
setTimeout(() => console.log("asdf"), 1000);
//# sourceMappingURL=asdf.js.map

All the HTML and ts will be called as "static" (no MVC)

Is it wrong to use http-server to see static HTML from my VS project? Is that the issue?

Should build any other way? Use a different setup?

I have a workaround (i.e., keep everything I need in the same TypeScript file), but it confuses me that I can't create and visualize a simple project with Node/TS.

Best Answer

Browsers don't support commonjs modules. You will need to use some tool (webpack, rollup, browserify) to bundle your modules after compilation.

If you remove the module option in the tsconfig.json or if you set to es2015 or esnext, the import and export statements will remain as they were in the original file.

import { foo } from './bar.js';

export default class Baz {}

It could work because some browsers already support native ES modules but it is necessary to add to the script tag a type attribute and set it to module.

<script type="module" src="foo.js"></script>

If you don't, you'll get some error like the 'SyntaxError: import declarations may only appear at top level of a module' one.