1

I want to play with ES6 modules, so I decided on using Node as a simple web server to avoid all CORS related errors I first encountered when doing it locally. Now I get MIME type related errors in the browser which I can't quite grasp.

Here is my server.js file:

const http = require('http'),
      url  = require('url'),
      fs   = require('fs');

http.createServer((req, res) => {

  const q = url.parse(req.url, true),
        filename = "." + q.pathname;

  fs.readFile(filename, (err, data) => {
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'});
      return res.end("404 Not Found");
    }  
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(data);
    return res.end();
  });

}).listen(8080);

If I try to access my index.html file in the browser, which contains the following code:

<!DOCTYPE html>
<html>
<body> 

  <!-- Works -->
  <script src="index.js"></script>

  <!-- Doesn't work -->
  <script src="module.js" type="module"></script>

</body>
</html>

I get the following error:

Failed to load module script: The server responded with a non-JavaScript MIME type of "text/html".

The type="text/javascript" attribute has no effect on the module tag either. I have read that all ES6 modules are "deferred" by default, meaning that they don't execute before the HTML is fully parsed. I guess this is the problem, but I don't know how to modify my server.js file accordingly to fix it. Help is greatly appreciated! If not too unpractical, I would prefer not to pull in any NPM packages.

EDIT: I thought that since I accessed http://localhost:8080/index.html That it was correct to send a header with type text/html. But if I console.log(url.parse(req.url, true)); I now see that deferred script tags trigger the createServer callback. The object logged explicitly shows it's the filename of the JS module.

I'll come back with a working example once I've fixed the faulty header.

SOLUTION: I pulled in two additional modules; path and mime. I used mime.getType() on the extension of the path returned by url.parse().

const http = require('http'),
      url  = require('url'),
      fs   = require('fs'),
      mime = require('mime'),
      path = require('path');

http.createServer((req, res) => {

  const q = url.parse(req.url, true),
        filename = "." + q.pathname;

  fs.readFile(filename, (err, data) => {
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'});
      return res.end("404 Not Found");
    }  
    res.writeHead(200, {'Content-Type': mime.getType(path.extname(filename))});
    res.write(data);
    return res.end();
  });

}).listen(8080);
Audun Olsen
  • 597
  • 6
  • 17

2 Answers2

1
res.writeHead(200, {'Content-Type': 'text/html'});

This would suggest that every file is served with a text/html mimetype.

Evert
  • 93,428
  • 18
  • 118
  • 189
  • 1
    This comment will reveal how much of a newbie I am at web servers, but anyways.. Since my URL always pointed to a html file, I thought that header would suffice. I decided to console log url.parse(req.url)` and saw that the createServer callback was triggered by module script-tags. Your comment put me on the right path, thanks! – Audun Olsen Feb 12 '19 at 23:18
  • 1
    @AudunOlsen Yea every 'file' is its own hidden HTTP request =) good luck on journey! – Evert Feb 13 '19 at 00:43
1

You're sending back a Content-Type: text/html header when the browser requests your module - this isn't valid, as per the HTML spec:

If any of the following conditions are met, throw a "NetworkError" DOMException:

  • response's type is "error"
  • response's status is not an ok status
  • The result of extracting a MIME type from response's header list is not a JavaScript MIME type

'Classic' scripts don't have this restriction, as far as I can tell.

In order to fix this, you need to send your JavaScript module back from the server with an appropriate MIME type, such as text/javascript:

res.writeHead(200, {'Content-Type': 'text/javascript'});

I'd recommend using something like Express instead of working directly with the http module, though - you can get a lot of this functionality out of the box, and I believe it'll handle setting the headers for you.

Joe Clay
  • 33,401
  • 4
  • 85
  • 85