2019年5月23日木曜日

NuxtでTypeScriptを使えるようにする

https://ja.nuxtjs.org/guide/typescript/
基本的に上記の手順で設定していけばOK

  1. モジュールの追加
  2. nuxt.config.jsのTypeScript化
  3. nuxt-property-decoratorの導入
  4. ESLintの設定

完成したプロジェクトは以下になります。
ご査収ください。
https://github.com/TAC/nuxt-typescript-example

環境

- version
OS Windows10
Node.js v8.15.0
Nuxt.js v2.7.1

1. モジュールの追加

yarn add -D @nuxt/typescript
yarn add ts-node

上記のモジュール追加後、空のtsconfig.jsonファイルを作成して、nuxtコマンドを実行するとデフォルト値で更新されます。

2. nuxt.config.jsのTypeScript化

nuxt.config.jsnuxt.config.tsにリネームします。
そしてドキュメントの手順通り以下を追記して、コンフィグの export 方法を変更します。

import NuxtConfiguration from  '@nuxt/config'
import pkg from './package'

const nuxtConfig: NuxtConfiguration = {
...
}

export default nuxtConfig

ただ、上記の変更だけだと下記のようなエラーが出るはずです。

 ERROR  ⨯ Unable to compile TypeScript:                                                                                                                                                                 23:58:04
nuxt.config.ts:2:17 - error TS2307: Cannot find module './package'.

2 import pkg from './package'
                  ~~~~~~~~~~~
nuxt.config.ts:51:9 - error TS2532: Object is possibly 'undefined'.

51         config.module.rules.push({
           ~~~~~~~~~~~~~


  nuxt.config.ts:2:17 - error TS2307: Cannot find module './package'.

  2 import pkg from './package'
                    ~~~~~~~~~~~
  nuxt.config.ts:51:9 - error TS2532: Object is possibly 'undefined'.

  51         config.module.rules.push({
             ~~~~~~~~~~~~~

  at createTSError (node_modules\ts-node\src\index.ts:240:12)
  at reportTSError (node_modules\ts-node\src\index.ts:244:19)
  at getOutput (node_modules\ts-node\src\index.ts:360:34)
  at Object.compile (node_modules\ts-node\src\index.ts:393:11)
  at Module.m._compile (node_modules\ts-node\src\index.ts:439:43)
  at Module._extensions..js (module.js:664:10)
  at Object.require.extensions.(anonymous function) [as .ts] (node_modules\ts-node\src\index.ts:442:12)
  at Module.load (module.js:566:32)
  at tryModuleLoad (module.js:506:12)
  at Function.Module._load (module.js:498:3)

Done in 3.26s.

まずはこちらのエラー

Cannot find module './package'.

TypeScriptはデフォルトではJSONファイルのImportを許可していないのでコンフィグで設定を変更する必要があるようです。
https://www.typescriptlang.org/docs/handbook/compiler-options.html
"resolveJsonModule": truetscofig.jsonに追記します。

{
  "compilerOptions": {
    "resolveJsonModule": true
  }
}

そしてImport時には拡張子まで指定するようにすればOK

import pkg from './package.json'

続いてはこちらのエラー

Object is possibly 'undefined'.

config.moduleundefined になってしまうことがあるのが原因なので、undefined の場合の処理を追記します。
(参考サイト) https://qiita.com/dora1998/items/932506fa995962d4dc63#nuxtconfigjs–nuxtconfigts

  /*
   ** Build configuration
   */
  build: {
    publicPath: '/assets/',
    
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {
      // Run ESLint on save
      if (ctx.isDev && ctx.isClient) {
+       if (!config.module) return
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue|ts)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  }

これでひとまず起動できるようになっているはずです。

3. nuxt-property-decoratorの導入

コンポーネントの記述を楽にするためにデコレータモジュールを導入します。
ドキュメントの手順ではvue-property-decoratorとなっているが、nuxt-property-decoratorを追加します。

yarn add nuxt-property-decorator

各コンポーネントのスクリプト部分をTypeScriptにします。

page/index.vue

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import Logo from '@/components/Logo.vue'

@Component({
  components: {
    Logo
  }
})
export default class extends Vue {}
</script>

components/Logo.vue

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'

@Component
export default class Logo extends Vue {}
</script>

クラスの定義が必要になるので少し冗長になります。
なおnuxt.config.tssrcDirを設定している場合は、tsconfig.jsonbaseUrlをそれに合わせないとコンポーネントの読み込みに失敗します。

4. ESLintの設定

以下のモジュールを追加する。

yarn add -D @typescript-eslint/eslint-plugin

そして、デフォルトで作成されている.eslintrc.jsを以下のように修正します。

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  parserOptions: {
    parser: '@typescript-eslint/parser'
  },
  extends: [
    '@nuxtjs',
    'plugin:nuxt/recommended',
    'plugin:prettier/recommended',
    'prettier',
    'prettier/vue'
  ],
  plugins: [
    'prettier',
    '@typescript-eslint'
  ],
  // add your custom rules here
  rules: {
    '@typescript-eslint/no-unused-vars': 'error'
  }
}

package.jsonlintスクリプトにts拡張子を追加しておきます。

"lint": "eslint --ext .js,.vue,.ts --ignore-path .gitignore .",

以上でNuxtでTypeScriptを使用できるようになりました。

Cloud FunctionsもTypeScriptにする

https://firebase.google.com/docs/functions/typescript?hl=ja
こちらの手順に従って進めればOKですが、いくつか追加で行う作業が必要でした。

まずはディレクトリ構成ですが、カレントディレクトリにアプリのビルド環境がある場合、予期せぬ動作が起こるようでした。

myproject
 +- src            # Nuxt src dir
 +- package.json   # Nuxt build script
 +- tsconfig.json  # Nuxt tsconfig
 +- nuxt.config.ts
 +- functions/
      +- package.json   # functions build script
      +- tsconfig.json  # functions tsconfig

上記の構成だと Nuxt を ビルドした際に functions 以下もビルドされてしまいコンフィグ設定の違いによりエラーが出てしまいます。
なので以下のような構成で Nuxtfunctions を配置します。

myproject
 +- app
 |    +- src            # Nuxt src dir
 |    +- package.json   # Nuxt build script
 |    +- tsconfig.json  # Nuxt tsconfig
 |    +- nuxt.config.ts
 +- functions/
      +- package.json   # functions build script
      +- tsconfig.json  # functions tsconfig

それぞれのディレクトリでビルドすることで干渉しなくなります。

次に app で追加したモジュールとExpressの型定義も追加しておきます。
また、lint が通らなくなるので、@nuxt/config を追加しておきます。

yarn add -D @nuxt/typescript @types/express
yarn add ts-node @nuxt/config

そして、公式の手順にあった firebase init functions で作成された functions/src/index.ts に色々と修正をいれて最終的に以下のようになりました。

import * as functions from 'firebase-functions'
import * as express from 'express'
import NuxtConfiguration from '@nuxt/config'
const { Nuxt } = require('nuxt')

const nuxtConfig: NuxtConfiguration = {
  dev: false,
  buildDir: 'nuxt',
  build: {
    publicPath: '/assets/'
  }
}
const nuxt = new Nuxt(nuxtConfig)

async function handleRequest(req: express.Request, res: express.Response) {
  res.set('Cache-Control', 'public, max-age=600, s-maxage=1200')
  await nuxt.ready()
  return nuxt.render(req, res)
}

const app = express()
app.use(handleRequest)

exports.ssr = functions.https.onRequest(app)

functions へ移動してビルドをすると functions/lib/index.js が出力されます。

cd functions/
yarn build

あとはアプリのビルドの成果物と合わせてローカルエミュレータを起動して動作確認ができると思います。

Written with StackEdit.

0 件のコメント:

コメントを投稿