素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

一つのファイルでサーバーとHTMLファイルとコードを共有するやつ

ちょっと説明がし難いがindex.htmlというファイルがあったとして、ファイルプロトコル経由で

file://path/index.html

などのように直接ブラウザで起動すると、プロトコルの関係でファイルが読み込めなかったり、エラーになったりすることがよくある。サーバーをたててlocalhostなどから読み込めばいいのだが、微妙にセットアップが面倒臭い。下記コードをindex.htmlとして保存すると

node index.html

とするだけでブラウザが立ち上がり、index.html自身をルートとしてそのディレクトリ以下のファイルを配信する簡易サーバーが立ち上がる。具体的には、

http://localhost:port/index.html

のような形でアクセスできるようになる。コマンドを実行したディレクトリの絶対パスをもとにポートを自動生成しているので、すでにサーバーが起動済みの場合、

node index.html

は新規にサーバーを立ち上げずに、既存のlocalhost:port/index.htmlをブラウザで開く。 特殊な魔法を使っているので、HTMLとしてもJSとしてもvalidで、ブラウザでもnodeでもコードの一部を共有している。

<!--
(function () { /* -->

<!DOCTYPE html>
<html>
  <head>
    <title>Title</title>
    <meta charset="UTF-8">
  </head>
  <body>
    <p>Hello!</p>
  </body>
</html>

<script src="index.html"></script>
<!-- */

// Shared among server and client sides
function mapPort(s) {
  var hashCode = 0;
  for (var i = 0; i < s.length; i++) {
    hashCode += hashCode * 31 + s.charCodeAt(i);
  }
  return 49152 + hashCode % 16384;
}

// Only for browser

if (typeof window !== 'undefined') {

  if (location.protocol == 'file:') {
    var url = 'http://localhost:' + mapPort(location.pathname);
    var req = new XMLHttpRequest();
    req.open('GET', url, false);
    try {
      req.send(null);
      location.href = url;
    } catch (ex) {
      document.writeln("<p>Server is not running. Please run the server by node index.html</p>");
    }
  }
  return;
}

// Only for Node.js

// Running a simple server.

var http = require('http'),
  fs = require('fs'),
  child_process = require('child_process'),
  path = require('path');

var ROOT_DIR = path.dirname(process.argv[1]);

function openInBrowser(url) {
  return child_process.spawnSync('open', [url]);
}

var port = mapPort(process.argv[1]);

var server = http.createServer(function (req, res) {
  var fPath = path.join(ROOT_DIR, req.url,
    /\/$/.test(req.url) ? 'index.html' : ''),
    file = null;
  try {
    file = fs.readFileSync(fPath);
  } catch (e) {
    // NOOP
  }
  res.writeHead(file ? 200 : 404);
  res.end(file || "File Not Found");
}).listen({port: port }, function () {
  console.log('successfully established');
  openInBrowser('http://localhost:' + port);
}).on('error', function () {
  console.log('already open?');
  openInBrowser('http://localhost:' + port);
});


})(); // -->