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.

2019年5月21日火曜日

firebase-tools v6.10.0 でNuxtのSSRアプリを動かすことができた!

下記の記事で動かないといってたものが動くようになりました!
https://blog.28go.jp/2019/05/firebase-tools_19.html

きっかけはこちらの記事です。
https://www.memory-lovers.blog/entry/2019/03/31/003456

https://nuxtjs.org/guide/release-notes#-strong-important-strong-migration-guide

どうやら Cloud Functions での Nuxt の呼び出し方が変わっていたようです。
上記の修正をすることで firebase-toolsv6.10.0 の ローカルエミュレータでも動作するようになりました。

ただ、functions のパッケージにいくつか追加するモジュールがあったので記載しておきます。

yarn add firebase-admin
yarn add -D firebase-functions-test

firebase-admin は使用していなくても必要になったようです。

以前検証に使ったリポジトリも修正しました。
https://github.com/TAC/nuxt-ssr-firebase-example

動かなくてもやもやしていたのでスッキリしました!

Written with StackEdit.

2019年5月19日日曜日

firebase-toolsをバージョンアップすると動かない

firebase-toolsをバージョンアップするとNuxtで作成したSSRアプリがfirebase serveで動かなくなったので、その対処法を記録しておきます。
結論としては「最新バージョンでは動かない」ということになります。

環境

Windows10
Node.js v8.15.0
Nuxt.js v2.7.1

検証にはcreate-nuxt-appでなにもモジュールを追加しないプロジェクトを作成して、Firebase Functionsで動作するようにちょっと修正した以下のプロジェクトを使用しました。
https://github.com/TAC/nuxt-ssr-firebase-example

6.9.0

そもそもhostingが起動しません。
6.9.1で修正された模様です。

6.9.1

functions実行時に以下のメッセージが表示されて動きません。

!  The Cloud Functions emulator requires the module "firebase-admin" to be installed as a dependency. To fix this, run "npm install --save firebase-admin" in your functions directory.
i  functions: Your functions could not be parsed due to an issue with your node_modules (see above)

使ってないのにfirebase-adminを要求されます。
とりあえずdevDependenciesに追加して、再度実行すると以下のエラーに変わります。

!  The Cloud Functions emulator requires the module "firebase-admin" to be installed. This package is in your package.json, but it's not available. You probably need to run "npm install" in your functions directory.
i  functions: Your functions could not be parsed due to an issue with your node_modules (see above)

これは6.9.2で修正されたようです。

6.9.2

今度はfirebase-functions-testを要求されるようになります。

!  The Cloud Functions emulator requires the module "firebase-functions-test" to be installed as a development dependency. To fix this, run "npm install --save-dev firebase-functions-test" in your functions directory.
i  functions: Your functions could not be parsed due to an issue with your node_modules (see above)

メッセージ通りdevDependenciesに追加して再実行すると、以下のエラーメッセージが表示される。

!  The Firebase Admin module has not been initialized early enough. Make sure you run "admin.initializeApp()" outside of any function and at the top of your code

使ってないのだが初期化が必要になっています。
これは6.10.0で修正されたようです。

6.10.0

6.10.0でいろいろ修正されていたので、起動できるようになっているかと期待したが、以下のエラーが解消できませんでした。
以前は、モジュールの読み込み等でNuxtが起動できていなくて同様のエラーが出ていたが、そうではないようです。

>  C:\work\repos\nuxt\example\functions\index.js:19
>        promise.then(resolve).catch(reject)
>                ^
>
>  TypeError: Cannot read property 'then' of undefined
>      at Immediate.nuxt.render.promise [as _onImmediate] (C:\work\repos\nuxt\example\functions\index.js:19:15)
>      at runCallback (timers.js:812:20)
>      at tryOnImmediate (timers.js:768:5)
>      at processImmediate [as _immediateCallback] (timers.js:745:5)

6.9.0以降はまともに動かなくなってしまった。

6.8.0

今のところちゃんと動く最新バージョンは6.8.0です。
このバージョンあたりからNode.js v6が非推奨になったので、--ignore-enginesのオプションをつけないと@google-cloud/functions-emulatorのインストールに失敗します。

https://github.com/googlearchive/cloud-functions-emulator/issues/267

yarn global add firebase-tools@6.8.0 @google-cloud/functions-emulator --ignore-engines

まだ、しばらくは6.8.0を使っていくしかないようです。

追記(2019-05-21)

解決しました!
詳細は下記の記事に記載してあります。
https://blog.28go.jp/2019/05/firebase-tools-v6100-nuxtssr.html

Written with StackEdit.

firebase-tools v6.10.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v6.10.0

  • database:instances:{create,list} now allow viewing and creating Realtime Database instances
  • Fixed issue with CORS rejecting some callable functions
  • Functions emulator no longer watches node_modules files
  • Functions emulator fails to route HTTPS functions to user-provided Express app
  • Functions emulator fails to provide correct req.path
  • Functions emulator fails on various malformed body requests
  • Functions emulator fails on Windows with EACCESS error
  • Functions emulator can now automatically initialize firebase-admin for simple use-cases
  • Fixed race condition where downloading emulators would sometimes resolve too early and fail
  • Fixed a number of issues that broke functions:shell
  • Fixed an issue where Firestore emulator would not start if rules file can’t be found

  • database:instances:{create,list} コマンドで Realtime Database インスタンスの表示と作成が可能になりました
  • 一部の呼び出し可能関数を拒否したCORSに関する問題を修正しました
  • 関数エミュレータは node_modules ファイルを監視しなくなりました
  • 機能エミュレータがHTTPS機能をユーザー提供のExpressアプリにルーティングできない
  • 関数エミュレータが正しい req.path を提供できない
  • 関数エミュレータがさまざまな不正な形式の本体要求で失敗する
  • Windows上でEACCESSエラーで関数エミュレータが失敗する
  • 関数エミュレータは単純なユースケースのためにfirebase-adminを自動的に初期化するようになりました
  • エミュレータをダウンロードすると早く解決しすぎて失敗することがあるという競合状態を修正しました
  • functions:shellを壊したいくつかの問題を修正しました
  • ルールファイルが見つからないとFirestoreエミュレータが起動しない問題を修正しました

Written with StackEdit.

2010年5月13日木曜日

jQueryである要素のイベントをすべて表示する方法

とりあえずどんなイベントが起こってるのかを見たい時に。

var arr = ["blur", "focus", "focusin", "focusout", "load",
           "resize", "scroll", "unload", "click", "dblclick",
           "mousedown", "mouseup", "mousemove", "mouseover",
           "mouseout", "mouseenter", "mouseleave", "change",
           "select", "submit", "keydown", "keypress", "keyup",
           "error"];
$.each(arr, function() {
  var type = this+"";
  $("option").bind(type, function(event) {
    $("pre[id='debug']").append(event.type + ":"
                                + event.target.nodeName
                                + "<br>");
  });
  $("select").bind(type, function(event) {
    $("pre[id='debug']").append(event.type + ":"
                                + event.target.nodeName
                                + "<br>");
 });
});