Node.jsでアルファベット混じりの短めのユニークID生成
Node.jsでおなじみのuuidパッケージを使ってUUIDを生成していました。
https://www.npmjs.com/package/uuid
ただこれだと
23b7f61b-5d92-40bf-9c2d-1a6da91ac059
のように長くなってしまいます。URLに使うなら長さが短くアルファベットも交えたものにしたい。noteの記事のURLのように。 探していたらnanoidパッケージが良さそうなので使うことにしました。
https://github.com/ai/nanoid#custom-alphabet-or-size
いつものyarnでインストール。
$ yarn add nanoid
このようにするとa〜zの英小文字と数字で構成された10桁の文字列が得られます。
import { customAlphabet } from 'nanoid' const nanoid = customAlphabet('1234567890abcdefghijklmnopqestuvwxyz', 10) const id = await nanoid()
Apple Watchにビルド&インストールできない
Watch OS App開発時によく起きる問題。以下で大抵解決します。
・iPhone再起動
・Apple Watch再起動
・Xcode再起動
・iOSアプリをビルド&iPhoneへインストール
・iPhoneでアプリが動いている状態で、Watch OSアプリをビルド&Apple Watchへインストール
icons.js as it exceeds the max of 500KB
Nuxtプロジェクトをビルドすると以下のようなエラーが出てました。
${パス}/node_modules/bootstrap-vue/src/icons/icons.js as it exceeds the max of 500KB.
bootstrap-vueにあるicon.jsがデカすぎみたいです。nuxt.config.jsのbuildに以下を追加して解決。
build: {
babel: {
compact: true
},
Nuxtでstore.commitを呼び出すラッパー関数を作る
NuxtのPageやComponentからStoreに値を更新する時、以下のようにstore.commitを呼びます。
this.$store.commit("analytics.incrementClickCount")
サーバーサイドでのみ実行されるasyncDataではContextを通じて呼びます。
// Nuxt公式のサンプルではcontextの部分がappになっていて混乱するのですが、
// 実体はContextです。
async asyncData(context) {
context.store.commit("analytics.incrementClickCount")
}
store/analytics.jsは以下のように実装されているとします。
[store/analytics.js]
export const state = () => ({
clickCount: 0
})
export const mutations = {
incrementClickCount(state) {
state.clickCount++
}
}
このincrementClickCountを頻繁に呼ぶことがある場合、毎回、
this.$store.commit("analytics.incrementClickCount")
と書くのはめんどくさいと思うかもしれません。
これを、
this.$incrementClickCount()
のように呼べると楽ですよね。Pluginを作ってそれを実現してみます。
[plugins/analytics.js]
export default ({ context }, inject) => {
inject("incrementClickCount", () => {
context.store.commit("analytics.incrementClickCount")
})
}
nuxt.config.jsでこのPluginを有効にします。
[nuxt.config.js]
plugins: [
{ src: '~/plugins/analytics.js' }
// serverのみならmodeをserverにする。clientのみならmodeをclientにする
{ src: "~/plugins/analytics.js", mode: "server" }
]
これで、PageやComponentでthis.$incrementClickCount()で呼び出せるようになります。
Pluginの実装をTypeScriptでしたい場合は以下のようにします。
[plugins/analytics.ts]
import { Plugin } from '@nuxt/types'
// PageやComponentでthis.$incrementClickCount()と書いた時、
// トランスパイルエラーにならないよう宣言
declare module 'vue/types/vue' {
interface Vue {
$incrementClickCount(): void
}
}
// asyncData(context)などでcontext.$incrementClickCount()と書いた時、
// トランスパイルエラーにならないよう宣言
declare module '@nuxt/types' {
interface Context {
$incrementClickCount(): void
}
}
const myPlugin: Plugin = (context, inject) => {
inject("incrementClickCount", () => {
context.store.commit("analytics/incrementClickCount")
})
}
export default myPlugin
こちらが参考になります。
GCP FirestoreのEmulatorをKillしたい
以下のようにFirestoreのエミュレータを指定のポートで起動できます。
$ gcloud beta emulators firestore start --host-port=localhost:8081
起動した後、[firestore] Dev App Server is now running.と表示されます。そこでCtrl + Cを押すとターミナルが入力状態に戻るので、エミュレータは終了しているのかと思ったらバックグラウンドで動いたままになっていました。私の環境問題?なんにせよ、停止したい😫
そこでポートを指定して(ここでは8081)、プロセスをKillすることにしました。
$ kill -9 $(lsof -t -i:8081)
参考にした記事。ありがたや🙏
■
1つのプロジェクト成り立たせるためにGCPのApp EngineとCloud Functionsで動作させています。App Engine、Cloud Functions両方共通のコードがあります。この共通コードをどこに置くかで小一時間悩みました。
結論から言うと共通コードはリポジトリを分けて、git submoduleで取り込むことにしたのですが、その経緯を書きます。
最初は以下のようにしようと思いました。
common
└ **/*.js
appEngine
├ package.json
├ node_modules/**
└ **/*.js
cloudFunctions
├ package.json
├ node_modules/**
└ **/*.js
そしてappEngine、cloudFunctions以下のjsファイルから相対パスでcommon以下のjsをimport。
[appEngine/index.js]
import "../common/hoge.js"
しかし、common以下にはnode_modulesがない。appEngineやcloudFunctions以下のnode_modulesにあるモジュールをcommon以下のjsでimportすると、そんなものはないと怒られる。
[common/hoge.js]
import {Request, Response} from "express" -> expressはないとエラーになる
common以下にpackage.jsonを置いてnpm install, yarn installでcommon/node_modulesを作ると動くけど、common、appEngine、cloudFunctionsそれぞれのnode_modules以下のモジュールのバージョンが食い違うと上手く動作しなくなることがある。
というわけで以下のようにcommonは別リポジトリにして、それぞれgit submoduleで取り込むのが良いと判断しました。
appEngine
├ package.json
├ node_modules/**
├ common/**/*.js <- commonはgit submodule
└ **/*.js
cloudFunctions
├ package.json
├ node_modules/**
├ common/**/*.js <- commonはgit submodule
└ **/*.js
appEngineとcloudFunctionsが同じリポジトリで、異なるディレクトリでcommonを別々にgit submodleで取り込むこともできます。が、結局、appEngine、cloudFunctionsもリポジトリ分けましたけどね・・・
TypeScript、Jest、Webpackでimportの際、aliasを使えるようにする
モジュールをimportする際にパスが長くなる場合、aliasを使うことがあります。Node.jsの開発でTypeScriptでソースコードを書いて、Jestでテストして、Webpackでバンドルしてデプロイしている人も多いと思います。aliasの設定は各々の設定ファイルでする必要があります。また書き方がそれぞれ違います。
仮にプロジェクトのルートディレクトリを<rootDir>とします。<rootDir>/dir1/dir2/dir3以下のhoge.tsをimportする時、以下のようにディレクトリを~/dir3で省略したいとします。
import {hoge} from "~/dir3/hoge"
TypeScriptのtsconfig.jsonの場合
pathsに書きます。これは直感的でわかりやすいです。
{
"compilerOptions": {
"paths": {
"~/dir3/*": [
"./dir1/dir2/dir3/*"
]
}
Jestのjest.config.jsの場合
こちらは正規表現になります。また<rootDir>を明示的に書きます。
module.exports = {
moduleNameMapper: {
'^~/dir3/(.*)$': '<rootDir>/dir1/dir2/dir3/$1'
}
WebPackのwebpack.config.jsの場合
path.resolveを使います。
const path = require('path');
module.exports = {
resolve: {
alias: {
"~/dir3": path.resolve(__dirname, "./dir1/dir2/dir3")
}
ちなみに<rootDir>を~で指定できるようにしたいだけの場合は以下の通り。
const path = require('path');
module.exports = {
resolve: {
alias: {
"~": __dirname
}