Node.js * TypeScriptでサーバとフロントのソースを共通化する

文字列操作周りの共通関数などサーバとフロントで両方使用する部分を共通化して1ソースに収める方法。
webpack 使えば比較的簡単
ソースは以下
https://github.com/ninomae-makoto/share-code-between-server-and-client




環境



Visual Studio Code
Node.js (6〜)
Webpack 4
TypeScript 2.7


ディレクトリ構成



public/
    javascripts/
        /dist webpack出力先
    f_index.ts
routes/
    b_index.ts
common/
    common.ts

common.tsに共通処理を書いてf_index.ts(ブラウザ),b_index.ts(サーバ)から呼び出す。



プログラムソース



common.ts

export class Common {

  public static test() {
   console.log("test")
  }
}

index.pug
doctype html
html
  head
    meta(http-equiv="X-UA-Compatible", content="IE=edge")
    meta(charset='UTF-8')
    meta( name="viewport", content="width=device-width, initial-scale=1")
    meta( name="robots" content="noindex, nofollow" )
    title= title
    link( rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css")
    link(rel='stylesheet', href='/stylesheets/style.css')
    //- link(rel="icon" type="image/png" href="/images/favicon.png")
    script(src="https://cdn.jsdelivr.net/npm/vue")
    script(src="https://unpkg.com/element-ui/lib/index.js")

  body
   script(src="./javascripts/dist/index.built.js")
   h1 サーバ・クライアント共通化


サーバ側の処理
b_index.ts
import * as express from "express"
import { Common } from "./../common/common"
{
  const router = express.Router()

  /* GET home page. */
  router.get("/", ({ }, res, { }) => {
    Common.test()
    res.render("index")
  })

  module.exports = router
}


フロント側の処理
f_index.ts
import { Common } from "./../../common/common"

Common.test()


サーバ・フロントそれぞれでCommon.test()を呼び出している。


Webpack設定ファイル
webpack.config.js
var path = require('path')
var webpack = require('webpack')
var HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

module.exports = {
  entry: {
    index: './public/javascripts/f_index.ts',
  },
  output: {
    path: path.resolve(__dirname, './public/javascripts/dist'),
    publicPath: '/dist/',
    filename: '[name].built.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve("src"),
        use: [
          {
            loader: "thread-loader",
            options: {
              workers: require('os').cpus().length - 1,
            }
          }
          // your expensive loader (e.g babel-loader)
        ]
      },
      {
        enforce: 'pre',
        test: /\.ts$/,
        loader: 'tslint-loader',
        exclude: /(node_modules)/,
        options: {
          configFile: 'tslint.json'
        }
      },
      {
        test: /\.ts$/,
        exclude: /node_modules|vue\/src/,
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/],
          transpileOnly: true
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // Since sass-loader (weirdly) has SCSS as its default parse mode, we map
            // the "scss" and "sass" values for the lang attribute to the right configs here.
            // other preprocessors should work out of the box, no loader config like this necessary.
            // 'scss': 'vue-style-loader!css-loader!sass-loader',
            // 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
            'ts': 'ts-loader!tslint-loader',
          }
          // other vue-loader options go here
        }
      }
    ]
  },
  resolve: {
    extensions: ['.ts', '.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true
  },
  externals: {
    "vue": "Vue"
  },
  cache: true,
  performance: {
    hints: false
  },
  devtool: '#eval-source-map'
}

module.exports.plugins = (module.exports.plugins || []).concat([
  new HardSourceWebpackPlugin(),
])

気にするところはentryとoutputくらい
詳細は以下
https://trueman-developer.blogspot.jp/2018/02/typescript.html
https://trueman-developer.blogspot.jp/2018/03/webpack3webpack4.html

tsconfig.json
{
  "compilerOptions": {
    "outDir": "./built/",
    "sourceMap": true,
    "strict": true,
    "noImplicitReturns": true,
    "module": "es2015",
    "moduleResolution": "node",
    "target": "es5"
  },
  "include": [
    "./public/javascripts/*",
    "./public/javascripts/**/*"
  ],
  "exclude": []
}

クライアントのトランスパイル用
webpackから呼ばれる

tsconfig_node.json
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "target": "ES5",
    "module": "commonjs",
    "sourceMap": true,
    "declaration": true,
    "inlineSources": true,
    "moduleResolution": "node",
    "rootDir": ".",
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "forceConsistentCasingInFileNames": true,
    "newLine": "LF",
    "lib": [
      "dom",
      "es5",
      "es2015.promise"
    ]
  },
  "include": [
    "routes/*",
    "utils/*",
    "test/*",
    "devtools/*"
  ],
  "exclude": []
}

サーバのトランスパイル用


これでCommon.test()がサーバとブラウザで呼ばれ両方のコンソールに"test"が出力される。

2018年3月28日水曜日