8

I'm starting a new project in which I'd like to have ES6 style modules, however, I can't get it to run in a browser. I'm using Chrome.

I isolated the issue into very few lines of code.

Here are my 2 TypeScript files:

app.ts

import { Component } from './component';

var component: Component = new Component();

component.ts

export class Component {

}

Here's how they compile to JavaScript:

app.js

import { Component } from './component';
var component = new Component();

component.js

export class Component {
}

My index.html only contains a script tag. I tried a few variations, but none of them worked.

index.html #1

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

The script is not loaded. (No request in network tab)

index.html #2

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

The script is not loaded. (No request in network tab)

index.html #3

<script src="src/app.js"></script>

Uncaught SyntaxError: Unexpected token {

I'm using tsc to transpile TypeScript via Visual Studio Code.

tasks.json

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "typescript",
            "tsconfig": "tsconfig.json",
            "problemMatcher": [
                "$tsc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "reveal": "silent"
            }
        }
    ]
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "sourceMap": false,
    "removeComments": false,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "preserveConstEnums": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "outDir": "../src/"
  },
  "exclude": [
    "logs",
    "node_modules"
  ]
}
Omid Nikrah
  • 2,444
  • 3
  • 15
  • 30
Royi Bernthal
  • 882
  • 4
  • 17
  • 45
  • It's simple: they don't – Victor Oct 27 '18 at 16:03
  • 1
    Are you sure? If so, what am I misunderstanding in here? https://www.contentful.com/blog/2017/04/04/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling/ – Royi Bernthal Oct 27 '18 at 16:10
  • 1
    Yes you can use modules directly in the latest browsers, without a bundler! You should transpile ts to js files without defining any specific module system. Then you can load modules in the browser with ` – Kokodoko Oct 27 '18 at 16:19
  • Ok I'm doing it now and the js looks cleaner, but the script still isn't loaded. I added more info at the bottom of the original question. – Royi Bernthal Oct 27 '18 at 16:28
  • have you tried writing a minimum constructor? [TS classes](https://www.typescriptlang.org/docs/handbook/classes.html) – Luca Borrione Oct 27 '18 at 18:03
  • I just tried this if this is what you meant, same results: export class Component { constructor() { } } – Royi Bernthal Oct 27 '18 at 18:10
  • In your transpiled `app.js` change your `import` statement to the following: `import { Component } from './component.js';` _Note: the file extension `.js` has been added to the Module Specifier_. The _ECMA-262 Imports Syntax_ only defines the syntax for modules and not the specific mechanism(s) for loading them. So the requirement for _with_ or _without_ a `.js` suffix may vary per implementation. However the inclusion of `.js` file extension is required in _Safari_ browser, and most probably in _Chrome_ too. Also, utilize `` in _index.html_ – RobC Oct 29 '18 at 10:31
  • Also worth noting that _Chrome_ supports `.mjs` file extension in the _Module Specifier_ too (as noted [here](https://developers.google.com/web/fundamentals/primers/modules#specifiers)), however the `.mjs` file extension _does not_ work with the latest _Safari_ browser. – RobC Oct 29 '18 at 11:16
  • I tried this as well. It's true depending on your environment, but it wouldn't be the point here since no request to the script is even made, it's not like I have a request with a bad path which would've been easy to debug. See my last comment to ymz. – Royi Bernthal Oct 30 '18 at 13:43

1 Answers1

11

To be honest - I think this is a good question because JS is widely use in both server-side and client-side application, which contributes to the already existing confusion among developers

It's clear that your TS code is written as server-side code (probably on Node.js). Trying to run it (as is) on client-side is... well.. tricky. The reason: The syntax you are using in your code suppose to run on server-side (not on client-side). Is there a workaround? Well... yes!

The good news:

JS ES6 does have a native module loader! (check MDN)

The bad ones:

  • Syntax is different from Node.js module loader syntax (when exporting a module)
  • Support is very partial (modern browsers only)

Some additional notes:

  • The common syntax of modules loading is associated with a third-party library called require js (https://requirejs.org/). You can use this library in client side projects but you have to install it and configure it properly (the docs are pretty clear about how to do that)
  • You can always use a task runner like grunt (https://gruntjs.com/) or similar projects to assist you to minify and unify all your code to a single file in production. Why you ask? It will clearly help you ease the load when a client fetch you website (less files are better in terms of network traffic)

As you see, there is no quick or simple answer to your question.. but it may be a good starting point to improve your knowledge and building better modern web apps.

UPDATE

As requested, I created a little sandbox demo that shows how to use ES6 native module (https://codesandbox.io/s/oj2rwm9v35)

index.html

<html>
<body>
    <div id="app"></div>
    <script type="module" src="src/primary.js"></script>
</body>
</html>

primary.js

import test from "./test";

test();

test.js

export default function test() {
  document.querySelector("#app").textContent = "Hello JS module!";
}
benbotto
  • 2,291
  • 1
  • 20
  • 32
ymz
  • 6,602
  • 1
  • 20
  • 39
  • Thanks for the answer. Is it still relevant to the updated question description? – Royi Bernthal Oct 27 '18 at 18:23
  • I believe it is.. your options are: use native ES6 modules (and change the way you export your functions), install `require` (and configure it properly) or simply decide to use a task runner to help you convert TS to a single minified JS file that will be suitable to client-side environment – ymz Oct 27 '18 at 18:42
  • 2
    I'm trying to use native ES6 modules. What's the difference between what I'm doing there and how it's supposed to be? The following import: import { Component } from './component'; seems to be the correct syntax. Also changing to import { Component } from './component.js'; doesn't make a difference. How come app.js isn't even loaded? I couldn't really understand from your answer, it'd be great if you could elaborate on that point. – Royi Bernthal Oct 27 '18 at 19:12
  • Thanks for the elaboration, so I don't think the problem is with the code itself. Does this work for you when you open it in your local file system? For me it doesn't. Both your code and mine work only when ran on a server. Any idea why? – Royi Bernthal Oct 30 '18 at 13:39
  • You are right. It works when running from localhost rather than from local file system. Also note that I defined a main module (which behaves like app module) and tried to load more modules from there - this approach (defining your app as a module, load it from a script tag and load other modules from this app module) should do the trick.. in my opinion at least – ymz Oct 31 '18 at 16:29