お知らせ!Nipoの姉妹アプリ「Maroud」がリリースされました

Quasarの古いプロジェクトをComposition-API化してTypeScriptにも対応させる

この記事は約10分で読めます。

2020年はVue3がリリースされ、Vue界隈に衝撃をもたらしました。2021年になって周りを見渡して見ると、Vue用に作られた様々なフレームワークやライブラリなどはVue3にまだ対応できていないのが現状のようです。

Vue3の導入はまだ時期尚早というのが私個人の見解です。しかしVue2をVue3風に記述できるComposition-APIは安定しているので、今回はMaroudの姉妹アプリであるNipoのコードを、Composition-API化、及びTypeScript対応させるまでの作業を備忘録として残したいと思います。

Quasarのプロジェクトを参考にComposition-API化を実行

Nipoのプロジェクトは2年ほど前になります。Javascript界隈で2年はかなり古いです。当時Quasar Cliを使ってプロジェクトを作成した際は、Composition-APIが入っていませんでした。

Composition-AIP非対応のプロジェクトをComposition-API対応のプロジェクトへアップデートします。

yarn add @vue/composition-api

src/bootフォルダ内に、composition-api.jsファイルを作成します。ファイルの中はこのような感じにします。

// src/boot/composition-api.jsファイルだよ

import VueCompositionApi from '@vue/composition-api'
import { boot } from 'quasar/wrappers'

export default boot(({ Vue }) => {
  Vue.use(VueCompositionApi)
})

quasar.conf.jsファイルのbootに、composition-apiを追記します

boot: [
  'axios',
  'composition-api' // コレを追加する
]

あとはターミナルから quasar devと入力することで、プロジェクトが起動できます。

QuasarのプロジェクトをTypeScript対応化する

公式サイトに手順が書いてあるので、そちらを参考に操作しました。ターミナルからコマンドを入力して必要なパッケージをDLします。

yarn add --dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

dd

プロジェクトルートにtsconfig.jsonファイルを作成します。中身はこんな感じ

{
  "extends": "@quasar/app/tsconfig-preset",
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "src/**/*.js",
    "src-pwa/**/*.js"
  ],
  "exclude": [
    "node_modules"
  ],

  "compilerOptions": {
    "forceConsistentCasingInFileNames": false,
    "noImplicitAny": false,
    "baseUrl": ".",
    "target": "es5"
  }
}

quasar.conf.jsを開き、次のように書き換えます

    supportTS: true,

eslintrc.jsファイルを開き、中身をごっそりと書き換えます

const { resolve } = require('path');
module.exports = {
  root: true,

  parserOptions: {
    extraFileExtensions: ['.vue'],
    parser: '@typescript-eslint/parser',
    project: resolve(__dirname, './tsconfig.json'),
    tsconfigRootDir: __dirname,
    ecmaVersion: 2015, // Allows for the parsing of modern ECMAScript features
    sourceType: 'module'
  },

  env: {
    browser: true
  },

  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',

    'standard',
    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
    'plugin:vue/strongly-recommended'
  ],

  // required to lint *.vue files
  plugins: [
    '@typescript-eslint',

    'vue'
  ],

  globals: {
    ga: true, // Google Analytics
    cordova: true,
    __statics: true,
    Capacitor: true,
    process: true
  },

  // add your custom rules here
  rules: {
    // allow async-await
    'generator-star-spacing': 'off',
    // allow paren-less arrow functions
    'arrow-parens': 'off',
    'one-var': 'off',

    'import/first': 'off',
    'import/named': 'error',
    'import/namespace': 'error',
    'import/default': 'error',
    'import/export': 'error',
    'import/extensions': 'off',
    'import/no-unresolved': 'off',
    'import/no-extraneous-dependencies': 'off',
    'prefer-promise-reject-errors': 'off',

    // TypeScript
    quotes: ['warn', 'single', { avoidEscape: true }],
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    // allow debugger during development only
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',

    // 強化
    'vue/html-closing-bracket-spacing': 'error',
    'vue/v-slot-style': 'error',
    'max-lines': ['warn', { max: 700, skipBlankLines: false}],
    'vue/multiline-html-element-content-newline': 'error',
    'vue/no-multi-spaces': 'error',
    '@typescript-eslint/no-unused-vars': 'error',

    // 緩和 OFF
    'vue/max-attributes-per-line': 'off',
    'vue/singleline-html-element-content-newline': 'off',
    'vue/no-mutating-props': 'off',

    // 緩和 Warning
    'no-async-promise-executor': 'warn',
    '@typescript-eslint/no-unsafe-assignment': 'warn',
    '@typescript-eslint/no-unsafe-call': 'warn',
    '@typescript-eslint/no-unsafe-member-access': 'warn',
    '@typescript-eslint/no-empty-function': ["error", { "allow": ["arrowFunctions"] }]
  }
}

Typescript化すると大量のワーニングやエラーが発生しますが、これらの修正はおいおいやっていくことになります。ファイル拡張子なども .jsを .tsへ書き換えて行く必要がありますが、一度にやる必要は有りません。

TypeScript化へのハマりポイント

プロジェクトフォルダにsrc以外に余分なデータがあると、そちらもLintしてしまうため、関係のないフォルダは除外するように設定しなければなりません。

恥ずかしながらeslintignoreで指定するのかと思っていましたがどうも違うようで、tsconfig.jsonで指定するようです。プロジェクトに直接関係のあるsrc以下だけを読み込むように、incluedesを次のように書き換えることでうまく動きます。

{
  "extends": "@quasar/app/tsconfig-preset",
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "src/**/*.js",
    "src-pwa/**/*.js" // この行はPWAでビルドする際に必要になる。詳しくは後述
  ],
  "exclude": [
    "node_modules"
  ],

  "compilerOptions": {
    "forceConsistentCasingInFileNames": false,
    "noImplicitAny": false,
    "baseUrl": ".",
    "target": "es5"
  }
}

TypeScript化したらPWAモードでビルドできない場合

Quasar CliでPWAモードビルドをすると、エラーが発生してしまい、ハマりました。

quasar build -m pwa
0:0  error  Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: src-pwa/register-service-worker.js.
The file must be included in at least one of the projects provided

もしこのようなエラーが出た場合もやはり、tsconfig.jsonの設定が間違っています。src-pwaフォルダにjsファイルを作れるように、tsconfig.jsonのincludesに

“src-pwa/**/*.js”
の一文を追記してあげることで回避可能でした。

TypeScriptは一人で開発する際もあったほうがいいと痛感

Nipoのプロジェクト自体は結構古く、TypeScriptを使っていませんでした。当時、VueとTypescriptの相性が悪いと言われていたので、そもそもTypescriptは検討に入っていませんでした。

しかしMaroudを開発する際にComposition-API + Typescriptで開発したところ、思いの外相性がよく、開発効率が上がりました。

一人で開発するような小さなプロジェクトでも、TypeScriptの恩恵は結構感じられます。ソフトウェアの品質を保つ上でも、Typescriptは今後の主力となるのは間違い無さそうです。

今回の記事は、NipoをTypescript化するに当たり操作した手順や、躓いた点をまとめた個人的な備忘録となりますが、もし同じような思いをしている方がいればと思い、1つの記事にしてみました。

ここまで読んでくれてありがとうございますっ
Nipoに興味を持ちましたか?すぐ始められますよ

個人情報の入力も無し。ワンクリックで体験アカウントで利用可能です。そのまま正式アカウントへ昇格もOK。Nipoが使えないと思ったらブラウザを閉じるだけです

開発者ブログ
クラウド日報 Nipo