Express で書いてるコードを TypeScript 化した時に対処したこと
の続き。
今度は 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” を指定する
module.exports = { // ... target: 'node', // ... };
よく分かってないけど target には以下を指定できる。デフォルトは web。
- “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", ... }, ... }
参考記事
- webpackでnpmのbinつくっててfs.readFileSync is not a functionと言われる問題 - DRYな備忘録
- WebPackでnodeランタイム向けにビルドする | // sakura note
- TypeScriptSamples/imageboard at master · Microsoft/TypeScriptSamples · GitHub
- Samples · TypeScript の Express のサンプルアプリ
- “ImageBoard: A Node.js + Express + MongoDB application built using TypeScript on the server”