GAミント至上主義

Web Monomaniacal Developer.

IEで起こるJavaScriptエラーをpolyfill.ioを使ってなんとかする

シニアジョブでは、まだまだ古いPCを使っているシニアの方が多いのか、令和になったこの頃でも、残念ながらIEのアクセス数が無視できない程度に健在です。

JavaScriptのエラーが起きてしまうとその後の処理が止まってしまったり影響が多いため、なんとかしたいところ。

でもVueなども使っており、全部をIEでも動くコードに書き換えることは現実的ではないので、ポリフィルを使った対応を行っています。

ポリフィルとは

IEなど古いブラウザにない機能を追加するコードのこと。詳しくは下記。
Polyfill (ポリフィル) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN

polyfill.ioとは

選択したポリフィルをブラウザーに合わせて、必要なだけ配信してくれるサービスです。
生成したURLをscriptタグで読み込むだけで使用できます。

Chromeなどでアクセスするとコメントだけのファイルが返ってくるのが確認できます。

polyfill.io

それぞれの機能ではなく、es5などのセットもありますが、余計なコードを読み込ませるのを防ぐため今回はエラーを見て一つずつ追加しました。

今の所こんな感じ

    <script src="https://polyfill.io/v3/polyfill.min.js?features=Element.prototype.classList%2CPromise%2CIntersectionObserver%2CObject.entries%2CObject.keys%2CArray.prototype.forEach%2CNodeList.prototype.forEach"></script>

ポリフィルでは対応できないエラー

文法エラー(Syntax Error)は残念ながらポリフィルでは対応できません。
主にconst, letと、アロー関数があるので、それは書き換えが必要です。

const hensu = 1;
# var hensu = 1; に変更

アロー関数はthisが変わるのでちょっと注意が必要かも。

BabelやTypeScriptを使ってる場合はビルドオプションでなんとかできるはず。

動作確認の方法

Windowsがあれば、IE11と互換モードでIE9までテストできますがシニアジョブでは基本的にMacなのでそうは行きません。

今回はVirtualBoxとMS公式のVMを使いました。Windows10とIE11が動かせます。重い。
Virtual Machines - Microsoft Edge Developer

どうやって必要な機能を選ぶか

IEで該当のページを開きF12などで開発者ツールを開き、その「Console」タブを開きます。
こんな感じでエラーが出ると思います。

f:id:uyamazak:20200519104436p:plain
f:id:uyamazak:20200519103627p:plain

IEのコンソールのエラーはまじクソで該当の箇所を示してくれず、MS社員がどうやってDebugしてたのか謎の機能なので、行数と文章でなんとかあたりをつけます。
私が使いこなせてないだけかもしれませんが、使いこなす日はこないでしょう。

あたりを付けた機能を下記ページで選択してURLを発行して読み込ませます。
https://polyfill.io/v3/url-builder/

今回のエラーだとnowはおそらくDate.nowだろってことで追加したらエラー消えました。
forEachは、Array.forEachだろと思いましたがNodeListの方だったっぽい。でも両方使う機会が多いので追加しておきました。
Syntax Errorは上記のconst でした。


Polyfillを使った対応は最善では無いですが、IEも減ってきて、最新のJS機能を使いたいこの頃では、現実的な解決方法だと思います。

Cloud BuildのSlack通知をFirebase Functions + TypeScriptで作る

Cloud Buildは同じGCP製品だけあって非常にシンプルにFirebase(WEBしかやったことない)のデプロイを行えますが、ビルド成功、失敗時の通知機能はついてません。

cloud.google.com

Circle CIのように標準ではついていないので、下記の公式ドキュメントのように自分でFunctionsの関数作って行うのが一般的のようです。

https://cloud.google.com/cloud-build/docs/configure-third-party-notifications?hl=ja


でも今回はFirebaseを使ったプロジェクトだったため、Firebase Functions + TypeScriptで行いたいのでTypeScriptの勉強も兼ねて作ってみました。

Firebaseのプロジェクトについては省略
Cloud Functions に TypeScript を使用する  |  Firebase

公式のと違う点は、終了時にビルドにかかった時間を知りたかったので追加した点。
メッセージは面倒だったのでただのテキストにしてます。

Webook URL

Slack側でWebhookのURLを発行します。
slack.com


発行したURLはFirebaseなのでprocess.envは使わずconfigを使って格納しています。

詳しくは下記
環境の構成  |  Firebase

事前に下記のコマンドでセットしておきます。

firebase functions:config:set slack.webhook_url="https://hooks.slack.com/services/***/***/***"

必要なパッケージ

slackの他、ビルド時間を計算するためにdate-fnsを使用しているのでインストールします。

npm install @slack/webhook date-fns

date-fns - modern JavaScript date utility library

ソース

まだいろいろ改善の余地があるけど動いたやつ。
WORKINGはQUEUEDの後にすぐ来るのでスキップしてます。
swich文やめようかな。

slack.ts

import { pubsub, config } from 'firebase-functions'
import { IncomingWebhook } from '@slack/webhook'
import { differenceInSeconds, parseISO } from 'date-fns'

const WEBHOOK_URL = config().slack.webhook_url
const webhook = new IncomingWebhook(WEBHOOK_URL)

function createMessage (data: any): string {
  const status = data.status
  const repoName = data.substitutions?.REPO_NAME
  const startTime = data.startTime
  const finishTime = data.finishTime
  const buildSeconds = differenceInSeconds(parseISO(finishTime), parseISO(startTime))
  const logUrl = data.logUrl
  switch (status) {
    case 'QUEUED':
      return `${status}, repo: ${repoName}`
    case 'SUCCESS':
    case 'FAILURE':
    case 'INTERNAL_ERROR':
    case 'TIMEOUT':
    case 'CANCELLED':
      return `${status}, repo: ${repoName}, buildtime: ${Math.floor(buildSeconds / 60)}min ${buildSeconds % 60}sec \n${logUrl}`
    case 'WORKING':
    default:
      return ''
  }
}

export async function notifySlack (message: pubsub.Message): Promise<void> {
  const body = message.data ? Buffer.from(message.data, 'base64').toString() : null
  if (!body) {
    console.log('body is null')
    return
  }
  const data = JSON.parse(body)
  const text = createMessage(data)
  if (!text) {
    console.log(`${data.status} skipped`)
    return
  }
  await webhook.send({
    text: text
  })
}


↑notifySlackをindex.tsで読み込んでonPublish()に渡します。
topic名'cloud-builds'はCloud Buildを有効にすると勝手にできるやつなので変えることはなさそうなので直書きしてます。

index.tsの抜粋

import { pubsub } from 'firebase-functions'
import { notifySlack } from './slack'

//ほかのやつ
export const notifyBuild = pubsub.topic('cloud-builds').onPublish(notifySlack)

できたやつ

こんな感じで通知が来るようになりました
f:id:uyamazak:20200513181023p:plain



プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発

プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発

  • 作者:Boris Cherny
  • 発売日: 2020/03/16
  • メディア: 単行本(ソフトカバー)

@google-cloud/pubsubをTypeScriptで使おうとしたらエラー

@google-cloud/pubsub": "^1.7.2"を使い、tscしたところ下記のエラーが出た。

www.npmjs.com

node_modules/@grpc/grpc-js/build/src/call.d.ts:68:5 - error TS2416: Property '_write' in type 'ClientWritableStreamImpl<RequestType>' is not assignable to the same property in base type 'Writable'.
  Type '(chunk: RequestType, encoding: string, cb: WriteCallback) => void' is not assignable to type '(chunk: any, encoding: string, callback: (err?: Error | undefined) => void) => void'.
    Types of parameters 'cb' and 'callback' are incompatible.
      Types of parameters 'err' and 'error' are incompatible.
        Type 'Error | null | undefined' is not assignable to type 'Error | undefined'.
          Type 'null' is not assignable to type 'Error | undefined'.

68     _write(chunk: RequestType, encoding: string, cb: WriteCallback): void;
       ~~~~~~

node_modules/@grpc/grpc-js/build/src/call.d.ts:79:5 - error TS2416: Property '_write' in type 'ClientDuplexStreamImpl<RequestType, ResponseType>' is not assignable to the same property in base type 'Duplex'.
  Type '(chunk: RequestType, encoding: string, cb: WriteCallback) => void' is not assignable to type '(chunk: any, encoding: string, callback: (err?: Error | undefined) => void) => void'.
    Types of parameters 'cb' and 'callback' are incompatible.
      Types of parameters 'err' and 'error' are incompatible.
        Type 'Error | null | undefined' is not assignable to type 'Error | undefined'.
          Type 'null' is not assignable to type 'Error | undefined'.

79     _write(chunk: RequestType, encoding: string, cb: WriteCallback): void;

バージョンを確認した所、1.7.2が新しめだったので1.6.0に戻し、念の為node_modulesを削除して再インストールしたところ問題なくコンパイルできた。
www.npmjs.com


package.json

"dependencies": {
    "@google-cloud/pubsub": "^1.6.0",

2系がもう少しで出るらしいので、バタバタしてるのかな。