21
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TypeScript + webpackでサーバサイドもTypeScriptしようぜのはなし

Last updated at Posted at 2016-04-11

主はおっしゃいましたwebpackはフロントエンドのjavascriptにだけ与えられたものではないと。よろしい!ではサーバサイドもTypeScriptでwebpackでes5(or es6 or es2015(まさかり対策))にしようではないか。

  • 今回のサンプルはこちらに保存してありますm(_ _)m

サーバサイドnodejsにも型が欲しかった

そもそも、サーバサイドなら別に他の言語を使いなさいというのはもっともですが、IOレイヤーの処理のひとつにスクレイピングでデータを取得するという部分があって(もちろんseleniumでもいいんだが)、そいつをnightmareで書いていたので、これはもうwrapするレイヤーもnodejsで行くしかないなっという背景であります。

ただせっかくですし、サーバサイドですし、割りときっちりした型がほしいなぁと思いまして。それでTypeScriptをサーバサイドで使ってwebpackでbuildしようという試みです。

Building with webpack in server side

TypeScriptに進む前にまずはサーバサイドnodejsをwebpackでひとつのindex.jsとしましょう。適当にnpm initして、npm install --save-dev webpack(ぼくはglobal installを推奨したくないので、local installでpathを解決する方向でいきます。)
src/index.jsに簡単な関数を指定してmodule.exportsしただけのものが、こちら。まだこの段階ではただのwebpackを使ったbuildです。

Using nodejs modules

次に適当なnodejsのmoduleを使ってみます。ブラウザで動かせない且つ、今回のmodelに合わせるために、nightmareを使用します。npm installe nightmare --saveします。んでもって、model層の処理として簡単なdataを取得する処理を書きます。

//src/models/scraper.js
'use strict';

const Nightmare = require('nightmare');

class HtmlContent {
  constructor(url) {
    this.url = url;
  }

  getContent() {
    new Nightmare()
      .goto(this.url)
      .wait(5000)
      .evaluate(() => {
        return document.querySelector('body').innerHTML;
      })
      .end()
      .then((result) => {
        return result;
      });
  }
}
module.exports = HtmlContent;

んでもって、exportsされたmodelをそのままindex.jsにてexportsします。

//src/index.js
'use strict';

const Scraper = require('./models/scraper');

module.exports = {
  Scraper: Scraper
};

ここまでの処理がこちら

Building nodejs program by webpack

ここでおもむろにwebpackでbuildしてみる。

$(npm bin)/webpack
Hash: d86c13400a0572da64bc
Version: webpack 1.12.15
Time: 23ms
   Asset     Size  Chunks             Chunk Names
index.js  1.41 kB       0  [emitted]  main
    + 1 hidden modules

build自体は通る。しかし、ここでこんな風に成果物をrequireしてみると...


$node
> var index = require('./dist/index')
ReferenceError: nightmare is not defined

nightmareがundefinedだと。ファッ??っと言いたい。requireしてるだけやんと。(実際ぼくはいいたくなった) この原因はmoduleの解決がdefaultだとvarという値が設定されていてglobal変数から解決しようとするから起こっています(ということが調べていたらわかった)。っというわけで、moduleの解決にcommonjsを使ってみましょう。(やっとwebpack.config.jsに辿りつけた(;^ω^))

//webpack.config.js
'use strict';

const path = require('path');

module.exports = {
  target: 'node',
  externals: /^(?!^\.\/)/,
  context: path.join(__dirname, 'src'),
  entry: './index.js',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'index.js',
    libraryTarget: 'commonjs2'
  },
  resolve: {
    extensions: ['', '.js']
  }
};

ここで特に抑えておきたいのはlibraryTarget: 'commonjs2'と、externals: /^(?!^\.\/)/でありんす。

libraryTarget

こいつは、さっきのdefaultのvarをcommonjsに差し替えるってやつで、moduleの解決をcommonjsで解決してくれます。なのでこのオプションを設定するとnightmare is not definedがでなくなるます。

externals

externalsは除外対象なので、buildの対象外って感じ。今回はnode_modulesをnpmによって解決したいので、./で始まってないmoduleをexcludeしてます。

さて、ここまでの成果物がこちら

TypeScript

ふぅー、やっとtypescriptにたどりつけましたね。まずは、typescript環境が欲しいのでnpm install --save-dev typescript webpack ts-loader typingsしましょう。

---------- TypeScript化 -----------

  1. $(npm bin)/typings initでd.tsをgetできる環境を用意しときましょう。
  2. $(npm bin)/typings install nightmare --save --ambientnightmareのd.tsを取得。
  3. mv src/index.js src/index.ts && mv src/models/scraper.js src/models/scraper.ts
  4. ごにょごにょっとtypescriptしますw

まぁ、このTypeScript化には深い意味はないですw

webpack.config.js + ts-loader

ts-loaderから.tsファイルを読み込みます。単純にtsc --initしてts-loaderを使うとバラバラバラっとたくさんエラーが出ます。

$(npm bin)/webpack
ERROR in /Users/JumpeiArashi/tmp/serverside-typescript-with-webpack/typings/main/ambient/nightmare/index.d.ts
(121,3): error TS2300: Duplicate identifier 'export='.

ERROR in /Users/JumpeiArashi/tmp/serverside-typescript-with-webpack/typings/browser/ambient/nightmare/index.d.ts
(121,3): error TS2300: Duplicate identifier 'export='.

ERROR in ./models/scraper.ts
(17,8): error TS2339: Property 'end' does not exist on type 'Nightmare'.

nightmae.endが定義されてないのは残念ですが、typings/browser配下のindex.d.tsもbuild対象になってますねぇ。。。ts-loader側にexcludeしたいところですが、ts-loaderのREADMEを見ると、tsconfig.jsonに書けと書いてありますねぇ。っというわけで、tsconfig.jsonに

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": false
    },
    "files": [
      "src/index.ts",
      "typings/main.d.ts"
    ]
}

filesを指定する。ブラウザで動くようには作れてませんが、nightmareをブラウザで動かそうって人はいないと思うってのと、今回はサーバサイドTypeScript + webpackが目的なのでこれにて完了。.。o○(Nightmareのambient moduleにPR出そうかな)


2016/11/25 追記

Nightmare.end()も定義された(というかした!)

半年が経過したので、nightmareを2.xに上げました。またdevDependenciesもupgradeさせました。んでもってtyped-nightmareを書いてPRしました。これで.endの未定義エラーも発生しませんね!

なお前のrepositoryのworking directoryが残ってる人は(そんな人ほとんどいないと思いますが)、お決まりの rm -fr ./node_modules && npm install && typings install をお昼寝しながらやってくださいね。

21
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?