主はおっしゃいました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化 -----------
-
$(npm bin)/typings init
でd.tsをgetできる環境を用意しときましょう。 -
$(npm bin)/typings install nightmare --save --ambient
nightmareのd.tsを取得。 mv src/index.js src/index.ts && mv src/models/scraper.js src/models/scraper.ts
- ごにょごにょっと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
をお昼寝しながらやってくださいね。