Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tsc emits __esModule even for ModuleKind.None #14351

Closed
yume-chan opened this issue Feb 28, 2017 · 39 comments · Fixed by atomist-attic/rug-typescript-compiler#16
Closed

tsc emits __esModule even for ModuleKind.None #14351

yume-chan opened this issue Feb 28, 2017 · 39 comments · Fixed by atomist-attic/rug-typescript-compiler#16
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@yume-chan
Copy link
Contributor

TypeScript Version: 2.3.0-dev.20170227

Code

const typescript = require("typescript");

const result = typescript.transpileModule(`let s:string = "string"`, {
    compilerOptions: {
        target: "es5",
        module: typescript.ModuleKind.None // I don't need module!!
    }
});

console.log(result.outputText)

Expected behavior:

"use strict";
var s = "string";

Actual behavior:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true }); // I cannot run this in browsers!!
var s = "string";

I believe it's caused by #13709 that fixed #13709, but ModuleKind.None also uses CommonJS emit routine (module.ts#L14) so it's also affected.

@mhegazy
Copy link
Contributor

mhegazy commented Feb 28, 2017

TranspileModule treats the input as a module regardless of the module option; hence the name of the function.

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 28, 2017
@HerringtonDarkholme
Copy link
Contributor

transpileModule transpiles module as the name. To run in browser you can simply declare a global variable exports.

@yume-chan
Copy link
Contributor Author

yume-chan commented Feb 28, 2017

@mhegazy Is there any way to not compile as modules using compiler API?

@HerringtonDarkholme You mean declare exports in every file? I think I get you, by declare global variable in html file.

@Johnz86
Copy link

Johnz86 commented Feb 28, 2017

I have similar problem.

Today I made an attempt to update our typescript version from 2.1.4 to 2.2.1. We have a stack with electron/react/redux/typescript.
After update to newer version Object.defineProperty(exports, "__esModule", { value: true }); appeared in compiled files. And we get Uncaught ReferenceError: exports is not defined in browser window.

This forced me to roll back to previous version, where I do not have this additional line after compilation.

@rhysd
Copy link
Contributor

rhysd commented Mar 1, 2017

I have this problem too on Electron app with typescript 2.2 .

To run in browser you can simply declare a global variable exports.

It looks too hacky... TypeScript compiler inserts Object.defineProperty(...) line just after use strict. So adding (window as any).exports = {} at head of file does not work.
Even adding var exports = {} to head of file is also impossible because of bellow error:

> tsc --pretty -p .


4 var exports = {};
      ~~~~~~~

renderer/index.ts(4,5): error TS2441: Duplicate identifier 'exports'. Compiler reserves name 'exports' in top level scope of a module.

rhysd added a commit to rhysd/Tui that referenced this issue Mar 1, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Mar 1, 2017

Just for clarification, the code resulting from traspileModule is not a module? you do not use WebPack/browserify? nor do you wrap the result in AMD/UMD functions? so how did you handle imports in the past?

@rhysd
Copy link
Contributor

rhysd commented Mar 1, 2017

My tsconfig may be wrong. Below is mine:

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "removeComments": true,
    "preserveConstEnums": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noEmitOnError": true,
    "strictNullChecks": true,
    "target": "es2015",
    "sourceMap": true
  },
  "include": [
    "main/**/*.ts",
    "renderer/**/*.ts",
    "webview/**/*.ts",
    "typings/**/*.d.ts"
  ]
}

from here.

I'm not using browserify, webpack and AMD/UMD functions. Just compiled with tsc and imported it with require() function (commonjs).

@mhegazy
Copy link
Contributor

mhegazy commented Mar 1, 2017

If you are on node, then module.exports exist, or am i missing something?

@rhysd
Copy link
Contributor

rhysd commented Mar 1, 2017

yeah, module.exports exists, but exports does not exist. So the line causes an error.

@rhysd
Copy link
Contributor

rhysd commented Mar 1, 2017

I found that I need to use require('./index.js') rather than <script src="./index.js"> to handle it as commonjs code... I'm sorry.

@iradul
Copy link

iradul commented Mar 1, 2017

I have a similar issue with PhantomJS when running JS transpired from TS. There should be an option to totally disable generation of this line:

Object.defineProperty(exports, "__esModule", { value: true });

@iradul
Copy link

iradul commented Mar 1, 2017

Workaround is to add for example export = 0; at the top of your TS file. Got it from here.

@kenotron
Copy link
Member

kenotron commented Mar 2, 2017

This is a bug. We create a tiny bootstrap .ts file to be included inside a page itself, such a generated .js file should not rely on any modules. Yet, we're stuck with 2.1 until this is fixed.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 2, 2017

This is a bug. We create a tiny bootstrap .ts file to be included inside a page itself, such a generated .js file should not rely on any modules. Yet, we're stuck with 2.1 until this is fixed.

Do your files have any imports or exports?

@pe8ter
Copy link

pe8ter commented Mar 2, 2017

Whether or not you call this a bug it is still a breaking change in a minor version. We have to pin back at version 2.1.x until we figure out what to do.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 2, 2017

The behavior was there for modules with export default. now it is for all modules.

I would like to get some clarity on the issue and why was it working before, and breaking now.

@pe8ter
Copy link

pe8ter commented Mar 2, 2017

Apologies, I missed important context. This ticket is explicitly about transpileModule whereas my issue is with the compiler in general. The compiler always emits the __esModule line when before 2.2 it never did, which is a breaking change. ES modules export immutable bindings and our tests use Jasmine spies in a few cases which no longer work since we can't modify imported symbols.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 2, 2017

The compiler did this before, but only for default exports. the new change expands the emit for this property for all modules.. this is inline with other transpilers as well.

c:\test>type a.ts
export default 0;

c:\test>tsc --v
Version 1.8.10

c:\test>tsc a.ts --m commonjs  --t es5

c:\test>type a.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = 0;

We sure can add this to the breaking change documentation for the TS 2.2 release.

@pe8ter
Copy link

pe8ter commented Mar 2, 2017

I had no idea that that was a thing. Yeah, it should be listed in the docs as a breaking change.

@kenotron
Copy link
Member

kenotron commented Mar 2, 2017

Okay in reading this I found out that we do exports. Removing this now gets rid of this generated code. Some documentation around this would be helpful.

@fatcerberus
Copy link

I just noticed that passing ModuleKind.None to ts.transpileModule() results in the following diagnostic being emitted by my build tool:

   transpiling '@/scripts/main.js'...
      E: TS5047: Option 'isolatedModules' can only be used when either option '--module' is provided or option 'target' is 'ES2015' or higher..

Despite the error diagnostic, a CommonJS module (which is not what I wanted) is emitted anyway.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 6, 2017

If your code does not have any import and/or export, but you still see __esModule defined, then it is a bug, and it is tracked by #14456, and should be fixed by #14493.

@fatcerberus
Copy link

Is the isolatedModules error (posted above) when using ModuleKind.None fixed too?

@mhegazy
Copy link
Contributor

mhegazy commented Mar 6, 2017

Is the isolatedModules error (posted above) when using ModuleKind.None fixed too?

Not sure i see why this is relevant. the error is accurate.

@fatcerberus
Copy link

You're right, of course, transpileModule() only compiles modules by design. The wording is maybe a bit confusing though, because isolatedModules is added internally by the implementation of transpileModule(). In my case I had to read the TypeScript source to find out the cause of the error; my options didn't explicitly include isolatedModules.

Passing anything other than ModuleKind.None to the function makes the error go away, which also isn't obvious from the error message. So maybe the wording could be improved?

@mhegazy
Copy link
Contributor

mhegazy commented Mar 6, 2017

Sure. feel free to send a PR with your proposed error message enhancements.

@hansrwindhoff
Copy link

hansrwindhoff commented Apr 14, 2017

So in my case, the library I am using (npm: type-check) provides its own require function when used inside the browser,

<body>
    <script src="./type-check.js"></script>
    <script src="./index.js"></script>
</body>

It does that to enable importing the library via require. Thereby typescript is also happily finding the @types declaration file in node_modules.

In index.ts one can now...

import * as tchecker from 'type-check';

With module: 'none' setting in tsconfig.json, the compiler emits:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tchecker = require("type-check");

So to make this work, one has to add the exports object:


<body>
    <script src="./type-check.js"></script>
    <script>
        var exports = {};
    </script>
    <script src="./index.js"></script>

</body>

PS: Just read that it will be fixed in 2.3. Maybe this workaround helps someone.

@fatcerberus
Copy link

@hansrwindhoff Your issue was fixed in #14493 I believe; I assume the fix will be in the next TS release.

@hansrwindhoff
Copy link

hansrwindhoff commented Apr 14, 2017 via email

@mhegazy mhegazy closed this as completed May 1, 2017
@evil-shrike
Copy link

evil-shrike commented May 12, 2017

I've upgraded my project with AMD/ES3 targets from TS 2.1.6 onto 2.3.2 and now TS starts emiting exports.__esModule = true; for many (but not all) files in the project.

Resulting files are AMD:

define(["require", "exports", ... ], function (require, exports, ... ) {
    "use strict";
    exports.__esModule = true;
 });

I can't say the new behavior breaks anything, but I can't understand why this field __esModule is needed for AMD modules. Is it on purpose?

My tsconfig.json:

{
	"compileOnSave": true,
	"compilerOptions": {
		"module": "amd",
		"target": "es3",
		"sourceMap": true,
		"noEmitHelpers": true,
		"experimentalDecorators": true,
		"baseUrl": ".", // This must be specified if "paths" is.
		"paths": {
			// map runtime paths to compile-time paths
			"lib/*": [ "src/lib/*" ],
			"modules/*": [ "src/modules/*" ],
			"vendor/*": [ "src/vendor/*" ]
		},
		"typeRoots" : ["src/typings"]
	},
	"include": [
		"src/**/*.ts",
		"src/**/.*.ts", // TS ignores file names starting with dot by default
		"tests/**/*.ts",
		"tests/**/.*.ts"
	]
}

// cc @mhegazy

@zaggino
Copy link

zaggino commented May 12, 2017

I've noticed that this exports.__esModule doesn't get emitted when you don't use any imports/exports, which is great. But I've also noticed it gets emitted when you only use typing imports (which get removed during compilation) so you get exports.__esModule even without any imports/exports in the file. Is this intended ? @mhegazy

@aluanhaddad
Copy link
Contributor

@zaggino The __esModule flag is important for interoperability between various loaders and transpilers.

@zaggino
Copy link

zaggino commented May 14, 2017

@aluanhaddad But in one of my browser use-cases (quite edge one), this is a breaking change, could this be changed to:

if (typeof exports !== "undefined") Object.defineProperty(exports, "__esModule", { value: true });

otherwise this breaks in browsers with

VM8188:1 Uncaught ReferenceError: exports is not defined

@aluanhaddad
Copy link
Contributor

But it was never valid in the first place. If you are not writing a module then you can't use module syntax and if you are writing a module then you need a loader or a packager. Type declarations are available globally if the module that they describe can be loaded globally. If not the type declaration needs to be revised.

@zaggino
Copy link

zaggino commented May 14, 2017

@aluanhaddad I'm not writing a module but I'm using module syntax to reference my interfaces which are defined in another file. So the resulting file doesn't have any imports itself although the source has, as all interface imports get removed from the result. It'd be great if Object.defineProperty(exports could be removed too if only interface imports are present in the file.

Or is there another way of using interfaces defined in a different file that I could use for this?

@aluanhaddad
Copy link
Contributor

aluanhaddad commented May 17, 2017

Yes, there are several options.

You can not export them, effectively making them global. Then they will be available in the global type namespace.

// global
interface Named {
    name: string;
}
interface Identity {
    id: number;
}

You can place them in a namespace, export them from that namespaces, but not export the namespace itself.

// global
declare namespace interfaces {
    export interface Named {
        name: string;
    }
    export interface Identity {
        id: number;
    }
}

You can also follow the UMD pattern which is used by most @types packages to export the namespace for use by modules and make it available globally at the same time for non module consumers.

// UMD globally available from scripts but requiring import from modules
export = interfaces;
export as namespace interfaces;
declare namespace interfaces {
    export interface Named {
        name: string;
    }
    export interface Identity {
        id: number;
    }
}

@Esqarrouth
Copy link

Are we not supposed to use import in the client side? I've tried everything I've found and nothing works, I just get different problems.

I'm using Typescript 2.4.1 with Webstorm.

I've experimented with different config files and got different results like:

import { hey } from "./a2";
var a2_1 = require("./a2");
Object.defineProperty(exports, "__esModule", { value: true });

All of these failed to work.

@aluanhaddad
Copy link
Contributor

@goktugyil using import in client side code is a very common practice. Leaving aside the nascent native support for modules available in a small number of browsers, using them in a browser environment implies a transpilation step.

Since you are using TypeScript, you are already transpiling your code in order to execute it on JavaScript runtimes. However, that step is necessary but not sufficient. In addition to using a transpiler, you also need to incorporate a module tool, such as a loader or a bundler, into your application.

Then you can configure TypeScript to transpile your code into a format recognized by that tool or, in some cases, use a plugin that integrates TypeScript into that tool.

Some options include RequireJS, Browserify, SystemJS/JSPM, Webpack, and Rollup.

@l5oo00
Copy link

l5oo00 commented May 11, 2018

I think if (typeof exports !== "undefined") exports.__esModule = true; is better.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.