TypeScript - prevent from inserting magic code - how?

Viewed 137

I am VERY green with regard to TypeScript (so be kind), but I have been unable to figure out how to stop TypeScript from adding what appears to be "magic" code to the resulting Javascript after transpiling.

Whenever I use "tsc", no errors are reported, but Typescript appends the following as the last line in the Javasccript file:

export {};

... and when the Javascript file is executed (loaded in a browser) this results in the following error being reported by the console:

Uncaught SyntaxError: Unexpected token 'export'

The compiler options in tsconfig.json are:

"compilerOptions": {
     "target": "ES2015",
     "removeComments": false,
     "noImplicitAny": true,
     "noImplicitThis": true,
     "moduleResolution": "Node"
   },

I then try changing the compiler options to the following:

   "compilerOptions": {
     "target": "ES2015",
     "module":"CommonJS",
     "removeComments": false,
     "noImplicitAny": true,
     "noImplicitThis": true,
     "moduleResolution": "Node"
   },

Success - the export line is no longer present, but now, the following line is added to the top of the javascript file!:

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

... and (again) when the Javascript file is executed (loaded in a browser) this results in the following error being reported by the console:

Uncaught ReferenceError: exports is not defined

I also tried to change the script tag that loads the javascript produced by tsc to make use of type="module", i.e.:

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

... when the page is loaded locally, the console displays:

Access to script at 'file:///...' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

Access to script at 'file:///...' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

Failed to load resource: net::ERR_FAILED

Obviously, the browser is telling me the script cannot be loaded locally, but I typically do my JS debugging that way. It would be a royal pain to have to upload the script just to load it from a network resource!

Good grief -- all I want is the Javascript code WITHOUT any "magic code" added. A compiler option such as:

"excludeMagicCode": true

... would be ideal!

Obviously, I am missing something very basic; is there a compiler option that can prevent these magic code statements from littering the resulting Javascript?

Thanks in advance.

2 Answers

By adding "module":"CommonJS" you are telling TypeScript to generate Node.js-like JavaScript, which is passed properties from its wrapping function arguments on execution such as exports (hence why it added code expecting it).

By default, TypeScript is generating JavaScript to be EcmaScript6-module compliant however the HTML script tag doesn't try to read modular JavaScript by default. One thing you can try, after you remove the module config setting and rebuild, is add the type attribute with the value module explicitly in your HTML:

 <script type="module" src="./ts-build-example.js"></script>

This will allow the script to accept and run export (and import) statements in most modern browsers.

After doing more research and reading about this similar issue, it appears that tsc is doing its job, but the real issue (for me) is using (loading) the resulting Javascript locally; which could not be done.

Since I am using Visual Studio Code, I created a Powershell script (fix_files.ps1) within the project folder to remove the export statements from the Javascript tsc produces:

#
# Remove all lines from the given file that start
# with the given string ...
#
function remove_lines( [string]$file_spec, [string]$remove_str="export {" ) {

   #
   # A file spec is required ...
   #

   if ( [String]::IsNullOrEmpty( $file_spec ) ) {

      Write-Output "* File specification is required"

      return

   }

   #
   # String to remove is required ...
   #
   
   if ( [String]::IsNullOrEmpty( $remove_str ) ) {

       Write-Output "* String to remvoe is required"

       return

   }

   #
   # Exit if file not found ...
   #
   
   if ( -not [System.IO.File]::Exists( $file_spec ) ) {

      Write-Output "* File not found: $file_spec"

      return

   }

   #
   # Count the number of lines that start with the given string
   # and if any are found, remove those lines from the file ...
   #

   $line_count = Get-Content $file_spec | Where { $_.StartsWith( $remove_str ) } | Measure-Object

   if ( $line_count.Count -gt 0 ) {

      (get-content $file_spec) | Where { -not $_.StartsWith( $remove_str ) } | Set-Content $file_spec

   }

   Write-Output "- $file_spec, lines removed: $($line_count.Count)"

}

#
# Fix files ...
#

Write-Output "Fixing Javascript files:"

remove_lines( "file1.js" )
remove_lines( "file2.js" )

Using the Terminal in Visual Studio Code, I run the following commands:

> tsc
> .\fix_files.ps1

Not very elegant, but at least it allows me to develop locally without the need for a module bundler or setting up a local web host.

Related