Humanity

Edit the world by your favorite way

Windows での最速 Node.js 環境構築

というわけで Windows で Node.js 環境をセットアップする方法をまとめてみる(もしかして前からこれぐらい簡単だったのかもしれないけど…)。

1. Nodist をインストール

Node.js を直接インストールせず、まずは Nodist をインストールする。

github.com

今だと v0.8.8 が最新だった。

2. windows-build-tools をインストール

次に管理者権限で Powershell を起動*1して、npm install -g --production windows-build-tools する。

github.com

3. 環境変数を init.cmd に移す

※この手順は必要ではないけど、自分は何か*2インストールした後は精神衛生上やることにしてるだけです。

インストール後に PATH や NODIST_* といった環境変数が勝手に足されると思いますが、 それをレジストリで設定した cmd.exe のスタートアップ時に実行されるコマンド(自分の場合は %USERPROFILE%\init.cmd)に記述して、足された環境変数を削除します。 詳しくは以下を参照してください。

mattn.kaoriya.net

え、終わり?

これで Windows での Node.js 環境は整う。 3番目のステップは省略可能なので実質 Nodist と windows-build-tools のインストールの2ステップと言っていい。 バージョンを切り替えたかったら nodist global (バージョン) で切り替えることもできる。

しかし windows-build-tools をインストールするだけで Visual StudioPython を自動インストールしてくれるのは本当に助かる(python コマンドとかがインストールされる訳ではなく、あくまで Node のライブラリのビルドに必要なランタイムのみインストールされる)。

ちなみに自分は node.exe や npm.exe は cmd.exe から起動してる。 MSYS をメインのシェルとして使ってるけど、そちらは vim と git 用。 PATH を通せば MSYS からも使えるんだろうけど、何となくトラブルが起きそうでやってない。*3

問題起きたら

ここ見れば解決する(はず)。

github.com

npm のアップグレード

npm をアップグレードしたかったらこんなのもあるらしい。

github.com

こちらも管理者権限で Powershell か cmd.exe を開いて

npm install -g --production npm-windows-upgrade
npm-windows-upgrade

だけでアップグレードされるらしい。お手軽…

*1:cmd.exe でもできるのかもしれないけど、自分はやってない

*2:コマンドラインで使うアプリ

*3:MSYS から MSYS 外の .exe を実行したくないの意

Promise の処理の流れ

メモ。Twitter に書いたことをそのまんま引用。


function taskA() {
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}
function finalTask() {
    console.log("Final Task");
}

var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask);

上記コードを現した図(ここから引用)。

Express で書いてるコードを TypeScript 化した時に対処したこと

tyru.hatenablog.com

の続き。

今度は express-generator で自動生成されたバックエンドのコードを TypeScript 化してみる。

tsconfig.json に “allowJs”: true を指定する

express-generator のコードは bin/www から app.js を require() する形になっているが、 app.ts から app.js を生成したところ、app.js で module.exports = app しているのに 空の Object {} がエクスポートされてしまう。 おそらく bin/www も TypeScript の仕組みに乗っからないと(TypeScript でトランスパイルしないと) require() できないのだと何となく予測したので、bin/www も .ts 化しようと思った… けどめんどいので –allowJs(引数)または “allowJs”: true(tsconfig.json)というので .js ファイルもトランスパイルできるらしい。 あと bin/www は shebang も付いてるので外してやる必要がある。

{
  "compilerOptions": {
    ...
    "allowJs": true,
    ...
  },
  ...
}

ちなみに resolve の extensions に .js を指定しないと require() の時に .js を module として扱ってくれないので注意。

module.exports = {
  // ...
  resolve: {
    // note if using webpack 1 you'd also need a '' in the array as well
    extensions: ['.ts', '.js']
  },
  // ...
};

target: “node” を指定する

otiai10.hatenablog.com

module.exports = {
  // ...
  target: 'node',
  // ...
};

よく分かってないけど target には以下を指定できる。デフォルトは web。

configuration

  • “web” Compile for usage in a browser-like environment (default)
  • “webworker” Compile as WebWorker
  • “node” Compile for usage in a node.js-like environment (use require to load chunks)
  • “async-node” Compile for usage in a node.js-like environment (use fs and vm to load chunks async)
  • “node-webkit” Compile for usage in webkit, uses jsonp chunk loading but also supports build in node.js modules plus require(“nw.gui”) (experimental)
  • “electron” Compile for usage in Electron – supports require-ing Electron-specific modules.
  • “electron-renderer” Compile for electron renderer process, provide a target using JsonpTemplatePlugin, FunctionModulePlugin for browser environment and NodeTargetPlugin and ExternalsPlugin for commonjs and electron bulit-in modules. Note: need webpack >= 1.12.15.

webpack-node-externals を使って require() を展開しないようにする

webpack config のプロパティに externals というものがある。 これは 見つけた require() の中でトランスパイル時に require() 先のモジュールを展開するか(埋め込むか)決めるプロパティ。 externals にファイル名が指定されていれば require() は展開されず、実行時に node によって require される。

しかし一つ一つ指定していくのは面倒。 そこで webpack-node-externals というプラグインがあり、これがエクスポートする nodeExternals() という関数を実行して次のように externals に指定してやれば node_modules 以下のライブラリは一括で展開しないよう指定できる。

WebPackでnodeランタイム向けにビルドする | // sakura note

結果

前回のも含めて以下のようになった。

var path = require('path');
var glob = require('glob');
var nodeExternals = require('webpack-node-externals');

function getFrontendEntries() {
  // Move init.ts to the first element
  var INIT_TS = './src/front/init.ts';
  var entries = glob.sync("./src/front/**/*.ts").sort(function (a, b) {
    return a === INIT_TS ? -1 :
           b === INIT_TS ? 1 :
           (a > b ? 1 : a === b ? 0 : -1)
  });
  return entries;
}

function getBackendEntries() {
  // Transpile './src/back/(.+).[jt]s' into '$1'
  var entries = {};
  glob.sync("./src/back/**/*.?s").forEach(function (entry) {
    entries[entry.replace(/.*src\/back\/(.+)\.[jt]s$/, '$1')] = entry;
  });
  return entries;
}

module.exports = [
  // Frontend
  {
    // 前回の module.exports
    // http://tyru.hatenablog.com/entry/2017/02/19/210947

    entry: getFrontendEntries(),
    output: {
      path: path.resolve("./public/javascripts"),
      // filename: '[name].js'
      filename: 'bundle.js'
    },
    resolve: {
      // note if using webpack 1 you'd also need a '' in the array as well
      extensions: ['.ts', '.js']
    },
    module: {
      // loaders will work with webpack 1 or 2; but will be renamed "rules" in
      // future
      loaders: [
        {
          exclude: /(node_modules)/,
          test: /\.ts$/,
          loader: 'ts-loader'
        },
        {
          test: /\.html$/,
          loader: 'html-loader?minimize'
        },
        {
          test: /\.scss$/,
          loaders: ['style-loader', 'css-loader', 'sass-loader']
        }
      ]
    }
  },

  // Backend
  {
    // 今回追加するもの

    target: 'node',
    externals: [nodeExternals()],
    entry: getBackendEntries(),
    output: {
      path: path.resolve("."),
      filename: '[name].js'
    },
    resolve: {
      // note if using webpack 1 you'd also need a '' in the array as well
      extensions: ['.ts', '.js']
    },
    module: {
      // loaders will work with webpack 1 or 2; but will be renamed "rules" in
      // future
      loaders: [
        {
          exclude: /(node_modules)/,
          test: /\.[jt]s$/,
          loader: 'ts-loader',
          options: {
            transpileOnly: true
          }
        }
      ]
    }
  }
];

出力されるコードは必ず .js がつくので、npm start で起動するスクリプトにも .js を付けてやる。

{
  ...
  "scripts": {
    ...
    "start": "node ./bin/www.js",
    ...
  },
  ...
}

参考記事